<commit_before>// Copyright 2010 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.

// This package implements a Reader which handles reading \r and \r\n
// deliminated lines.
package line

import (
	"io"
	"os"
)

// Reader reads lines from an io.Reader (which may use either '\n' or
// '\r\n').
type Reader struct {
	buf      []byte
	consumed int
	in       io.Reader
	err      os.Error
}

func NewReader(in io.Reader, maxLineLength int) *Reader {
	return &Reader{
		buf:      make([]byte, 0, maxLineLength),
		consumed: 0,
		in:       in,
	}
}

// ReadLine tries to return a single line, not including the end-of-line bytes.
// If the line was found to be longer than the maximum length then isPrefix is
// set and the beginning of the line is returned. The rest of the line will be
// returned from future calls. isPrefix will be false when returning the last
// fragment of the line.  The returned buffer points into the internal state of
// the Reader and is only valid until the next call to ReadLine. ReadLine
// either returns a non-nil line or it returns an error, never both.
func (l *Reader) ReadLine() (line []byte, isPrefix bool, err os.Error) {
	if l.consumed > 0 {
		n := copy(l.buf, l.buf[l.consumed:])
		l.buf = l.buf[:n]
		l.consumed = 0
	}

	if len(l.buf) == 0 && l.err != nil {
		err = l.err
		return
	}

	scannedTo := 0

	for {
		i := scannedTo
		for ; i < len(l.buf); i++ {
			if l.buf[i] == '\r' && len(l.buf) > i+1 && l.buf[i+1] == '\n' {
				line = l.buf[:i]
				l.consumed = i + 2
				return
			} else if l.buf[i] == '\n' {
				line = l.buf[:i]
				l.consumed = i + 1
				return
			}
		}

		if i == cap(l.buf) {
			line = l.buf[:i]
			l.consumed = i
			isPrefix = true
			return
		}

		if l.err != nil {
			line = l.buf
			l.consumed = i
			return
		}

		// We don't want to rescan the input that we just scanned.
		// However, we need to back up one byte because the last byte
		// could have been a '\r' and we do need to rescan that.
		scannedTo = i
		if scannedTo > 0 {
			scannedTo--
		}
		oldLen := len(l.buf)
		l.buf = l.buf[:cap(l.buf)]
		n, readErr := l.in.Read(l.buf[oldLen:])
		l.buf = l.buf[:oldLen+n]
		if readErr != nil {
			l.err = readErr
		}
	}
	panic("unreachable")
}
<commit_msg>encoding/line: fix up a few typos and infelicities in the doc comments<commit_after>// Copyright 2010 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.

// The line package implements a Reader that reads lines delimited by '\n' or ' \r\n'.
package line

import (
	"io"
	"os"
)

// Reader reads lines, delimited by '\n' or \r\n', from an io.Reader.
type Reader struct {
	buf      []byte
	consumed int
	in       io.Reader
	err      os.Error
}

// NewReader returns a new Reader that will read successive
// lines from the input Reader.
func NewReader(input io.Reader, maxLineLength int) *Reader {
	return &Reader{
		buf:      make([]byte, 0, maxLineLength),
		consumed: 0,
		in:       input,
	}
}

// ReadLine tries to return a single line, not including the end-of-line bytes.
// If the line was found to be longer than the maximum length then isPrefix is
// set and the beginning of the line is returned. The rest of the line will be
// returned from future calls. isPrefix will be false when returning the last
// fragment of the line.  The returned buffer points into the internal state of
// the Reader and is only valid until the next call to ReadLine. ReadLine
// either returns a non-nil line or it returns an error, never both.
func (l *Reader) ReadLine() (line []byte, isPrefix bool, err os.Error) {
	if l.consumed > 0 {
		n := copy(l.buf, l.buf[l.consumed:])
		l.buf = l.buf[:n]
		l.consumed = 0
	}

	if len(l.buf) == 0 && l.err != nil {
		err = l.err
		return
	}

	scannedTo := 0

	for {
		i := scannedTo
		for ; i < len(l.buf); i++ {
			if l.buf[i] == '\r' && len(l.buf) > i+1 && l.buf[i+1] == '\n' {
				line = l.buf[:i]
				l.consumed = i + 2
				return
			} else if l.buf[i] == '\n' {
				line = l.buf[:i]
				l.consumed = i + 1
				return
			}
		}

		if i == cap(l.buf) {
			line = l.buf[:i]
			l.consumed = i
			isPrefix = true
			return
		}

		if l.err != nil {
			line = l.buf
			l.consumed = i
			return
		}

		// We don't want to rescan the input that we just scanned.
		// However, we need to back up one byte because the last byte
		// could have been a '\r' and we do need to rescan that.
		scannedTo = i
		if scannedTo > 0 {
			scannedTo--
		}
		oldLen := len(l.buf)
		l.buf = l.buf[:cap(l.buf)]
		n, readErr := l.in.Read(l.buf[oldLen:])
		l.buf = l.buf[:oldLen+n]
		if readErr != nil {
			l.err = readErr
		}
	}
	panic("unreachable")
}
<|endoftext|>


<commit_before>package cah

import (
	// "errors"
	"encoding/json"
	//"fmt"
	"github.com/gamelost/bot3server/server"
	"io/ioutil"
	// "log"
	"math/rand"
	"net/http"
	"strings"
	"time"
	"unicode"
)

// source URL for CAH card templates
const CAH_SOURCE_URL = "https://raw.githubusercontent.com/gamelost/bot3server/master/module/cah/cah-cards-standard.json"

type CahService struct {
	RandomNG          *rand.Rand
	CahCardCollection *CahCardCollection
}

type CahCard struct {
	Id         int64
	CardType   string
	NumAnswers int64
	Text       string
}

type CahCardCollection []struct {
	CardType   string `json:"cardType"`
	Expansion  string `json:"expansion"`
	Id         int64  `json:"id"`
	NumAnswers int64  `json:"numAnswers"`
	Text       string `json:"text"`
}

func (svc *CahService) NewService() server.BotHandler {

	var newSvc = &CahService{}

	newSvc.CahCardCollection = &CahCardCollection{}

	// set up the rng
	newSvc.RandomNG = rand.New(rand.NewSource(time.Now().UnixNano()))

	// download the CAH json file
	resp, err := http.Get(CAH_SOURCE_URL)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	json.Unmarshal(body, newSvc.CahCardCollection)

	return newSvc
}

func (svc *CahService) Handle(botRequest *server.BotRequest, botResponse *server.BotResponse) {

	strInput := parseInput(botRequest.RawLine.Text())
	if len(strInput) > 0 {
		botResponse.SetSingleLineResponse(svc.RandomCahMessageWithArgument(strInput))
	} else {
		botResponse.SetSingleLineResponse(svc.RandomCahMessage())
	}
}

func (svc *CahService) RandomCard() *CahCard {

	randVal := svc.RandomNG.Intn(svc.CahCardCollection.CardCount())
	return svc.CahCardCollection.GetCardAt(randVal)
}

func (svc *CahService) RandomQuestionCard() *CahCard {

	cCard := svc.RandomCard()
	for {
		if cCard.CardType == "Q" {
			break
		} else {
			cCard = svc.RandomCard()
		}
	}
	return cCard
}

func (svc *CahService) RandomOneAnswerQuestionCard() *CahCard {

	cCard := svc.RandomQuestionCard()
	for {
		if cCard.NumAnswers == 1 {
			break
		} else {
			cCard = svc.RandomQuestionCard()
		}
	}
	return cCard
}

func (svc *CahService) RandomAnswerCard() *CahCard {

	cCard := svc.RandomCard()
	for {
		if cCard.CardType == "A" {
			break
		} else {
			cCard = svc.RandomCard()
		}
	}
	return cCard
}

func (svc *CahService) MessageFromQuestionAndAnswers(questionStr string, answers []string) string {

	var finalStr string
	substrings := strings.Split(questionStr, "_")
	if len(substrings) < 2 {
		finalStr = questionStr + " " + convertToStandaloneAnswer(answers[0])
	} else {

		ansCounter := 0
		for _, value := range substrings {
			finalStr += value
			if ansCounter < len(answers) {
				finalStr += convertToInlineAnswer(answers[ansCounter])
				ansCounter++
			}
		}
	}

	return finalStr
}

func (svc *CahService) RandomCahMessage() string {

	qCard := svc.RandomQuestionCard()

	// find out how many answers we need
	numAnswers := qCard.NumAnswers

	// queue up all needed answer cards
	var answers = make([]string, numAnswers)
	for i := 0; i < int(numAnswers); i++ {
		answers[i] = svc.RandomAnswerCard().Text
	}

	return svc.MessageFromQuestionAndAnswers(qCard.Text, answers)
}

func (svc *CahService) RandomCahMessageWithArgument(argStr string) string {

	qCard := svc.RandomOneAnswerQuestionCard()

	// queue up all needed answer cards
	var answers = make([]string, 1)
	answers[0] = argStr

	return svc.MessageFromQuestionAndAnswers(qCard.Text, answers)
}

func (ccc CahCardCollection) CardCount() int {
	return len(ccc)
}

func (ccc CahCardCollection) GetCardAt(cardLoc int) *CahCard {
	return &CahCard{CardType: ccc[cardLoc].CardType, Id: ccc[cardLoc].Id, NumAnswers: ccc[cardLoc].NumAnswers, Text: ccc[cardLoc].Text}
}

func parseInput(input string) string {

	input = strings.TrimPrefix(input, "!cah")
	input = strings.Trim(input, " ")

	return input
}

func convertToInlineAnswer(orig string) string {

	runes := []rune(orig)
	runes[0] = unicode.ToLower(runes[0])
	orig = string(runes)
	return strings.TrimRight(orig, ".")
}

func convertToStandaloneAnswer(orig string) string {

	runes := []rune(orig)
	runes[0] = unicode.ToUpper(runes[0])

	// if no punctuation, add a period
	lastChar := runes[len(runes)-1]
	if lastChar == '.' || lastChar == '!' || lastChar == '?' {
		return string(runes)
	} else {
		return string(runes) + "."
	}
}
<commit_msg>If !cah contains __, it's a question.<commit_after>package cah

import (
	// "errors"
	"encoding/json"
	// "fmt"
	"github.com/gamelost/bot3server/server"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"regexp"
	"strings"
	"time"
	"unicode"
)

// source URL for CAH card templates
const CAH_SOURCE_URL = "https://raw.githubusercontent.com/gamelost/bot3server/master/module/cah/cah-cards-standard.json"

type CahService struct {
	RandomNG          *rand.Rand
	CahCardCollection *CahCardCollection
}

type CahCard struct {
	Id         int64
	CardType   string
	NumAnswers int64
	Text       string
}

type CahCardCollection []struct {
	CardType   string `json:"cardType"`
	Expansion  string `json:"expansion"`
	Id         int64  `json:"id"`
	NumAnswers int64  `json:"numAnswers"`
	Text       string `json:"text"`
}

func (svc *CahService) NewService() server.BotHandler {

	var newSvc = &CahService{}

	newSvc.CahCardCollection = &CahCardCollection{}

	// set up the rng
	newSvc.RandomNG = rand.New(rand.NewSource(time.Now().UnixNano()))

	// download the CAH json file
	resp, err := http.Get(CAH_SOURCE_URL)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	json.Unmarshal(body, newSvc.CahCardCollection)

	return newSvc
}

func (svc *CahService) Handle(botRequest *server.BotRequest, botResponse *server.BotResponse) {

	strInput := parseInput(botRequest.RawLine.Text())
	if len(strInput) > 0 {
		botResponse.SetSingleLineResponse(svc.RandomCahMessageWithArgument(strInput))
	} else {
		botResponse.SetSingleLineResponse(svc.RandomCahMessage())
	}
}

func (svc *CahService) RandomCard() *CahCard {

	randVal := svc.RandomNG.Intn(svc.CahCardCollection.CardCount())
	return svc.CahCardCollection.GetCardAt(randVal)
}

func (svc *CahService) RandomQuestionCard() *CahCard {

	cCard := svc.RandomCard()
	for {
		if cCard.CardType == "Q" {
			break
		} else {
			cCard = svc.RandomCard()
		}
	}
	return cCard
}

func (svc *CahService) RandomOneAnswerQuestionCard() *CahCard {

	cCard := svc.RandomQuestionCard()
	for {
		if cCard.NumAnswers == 1 {
			break
		} else {
			cCard = svc.RandomQuestionCard()
		}
	}
	return cCard
}

func (svc *CahService) RandomAnswerCard() *CahCard {

	cCard := svc.RandomCard()
	for {
		if cCard.CardType == "A" {
			break
		} else {
			cCard = svc.RandomCard()
		}
	}
	return cCard
}

func (svc *CahService) MessageFromQuestionAndAnswers(questionStr string, answers []string) string {

	var finalStr string
	substrings := strings.Split(questionStr, "_")
	if len(substrings) < 2 {
		finalStr = questionStr + " " + convertToStandaloneAnswer(answers[0])
	} else {

		ansCounter := 0
		for _, value := range substrings {
			finalStr += value
			if ansCounter < len(answers) {
				finalStr += convertToInlineAnswer(answers[ansCounter])
				ansCounter++
			}
		}
	}

	return finalStr
}

func (svc *CahService) RandomCahMessage() string {

	qCard := svc.RandomQuestionCard()

	// find out how many answers we need
	numAnswers := qCard.NumAnswers

	// queue up all needed answer cards
	var answers = make([]string, numAnswers)
	for i := 0; i < int(numAnswers); i++ {
		answers[i] = svc.RandomAnswerCard().Text
	}

	return svc.MessageFromQuestionAndAnswers(qCard.Text, answers)
}

func (svc *CahService) RandomCahAnswerMessage(s string) string {
	card := svc.RandomAnswerCard()
	return convertToInlineAnswer(card.Text)
}

func (svc *CahService) RandomCahMessageWithArgument(argStr string) string {

	log.Printf("We got %s", argStr)
	if strings.Contains(argStr, "__") {
		re, _ := regexp.Compile("__+")
		ret := re.ReplaceAllStringFunc(argStr, svc.RandomCahAnswerMessage)
		return ret
	} else {
		qCard := svc.RandomOneAnswerQuestionCard()

		// queue up all needed answer cards
		var answers = make([]string, 1)
		answers[0] = argStr

		return svc.MessageFromQuestionAndAnswers(qCard.Text, answers)
	}
}

func (ccc CahCardCollection) CardCount() int {
	return len(ccc)
}

func (ccc CahCardCollection) GetCardAt(cardLoc int) *CahCard {
	return &CahCard{CardType: ccc[cardLoc].CardType, Id: ccc[cardLoc].Id, NumAnswers: ccc[cardLoc].NumAnswers, Text: ccc[cardLoc].Text}
}

func parseInput(input string) string {

	input = strings.TrimPrefix(input, "!cah")
	input = strings.Trim(input, " ")

	return input
}

func convertToInlineAnswer(orig string) string {

	runes := []rune(orig)
	runes[0] = unicode.ToLower(runes[0])
	orig = string(runes)
	return strings.TrimRight(orig, ".")
}

func convertToStandaloneAnswer(orig string) string {

	runes := []rune(orig)
	runes[0] = unicode.ToUpper(runes[0])

	// if no punctuation, add a period
	lastChar := runes[len(runes)-1]
	if lastChar == '.' || lastChar == '!' || lastChar == '?' {
		return string(runes)
	} else {
		return string(runes) + "."
	}
}
<|endoftext|>


<commit_before>package gorma

const modelTmpl = `// {{if .TypeDef.Description}}{{.TypeDef.Description}}{{else}}app.{{ .TypeName}} storage type{{end}}
// Identifier: {{ .TypeName}}
type {{.TypeName}} {{ modeldef .TypeDef }}
{{ $typedef := .TypeDef  }}
{{ $dynamictable := .DoDynamicTableName }}
{{ $typename  := .TypeName }}
{{ $cached := .DoCache }}
{{ if .DoCustomTableName }}
func (m {{$typename}}) TableName() string {
	return "{{ .CustomTableName}}"
}
{{ end }}
{{ if .DoRoler }}
func (m {{$typename}}) GetRole() string {
	return m.Role
}
{{end}}

type {{$typename}}Storage interface {
	DB() interface{}
	List(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}) []{{$typename}}
	One(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, id int) ({{$typename}}, error)
	Add(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, o {{$typename}}) ({{$typename}}, error)
	Update(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, o {{$typename}}) (error)
	Delete(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, id int) (error)
{{ range $idx, $bt := .BelongsTo}}
	ListBy{{$bt.Parent}}(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, parentid int) []{{$typename}}
	OneBy{{$bt.Parent}}(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, parentid, id int) ({{$typename}}, error)
{{end}}
	{{ storagedef $typedef }}
}
type {{$typename}}DB struct {
	db gorm.DB
	{{ if .DoCache }}cache *cache.Cache{{end}}
}
{{ range $idx, $bt := .BelongsTo}}
func {{$typename}}FilterBy{{$bt.Parent}}(parentid int, originaldb *gorm.DB) func(db *gorm.DB) *gorm.DB {
	if parentid > 0 {
		return func(db *gorm.DB) *gorm.DB {
			return db.Where("{{ $bt.DatabaseField }}_id = ?", parentid)
		}
	} else {
		return func(db *gorm.DB) *gorm.DB {
			return db
		}
	}
}

func (m *{{$typename}}DB) ListBy{{$bt.Parent}}(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, parentid int) []{{$typename}} {

	var objs []{{$typename}}
	m.db{{ if $dynamictable }}.Table(tableName){{ end }}.Scopes({{$typename}}FilterBy{{$bt.Parent}}(parentid, &m.db)).Find(&objs)
	return objs
}

func (m *{{$typename}}DB) OneBy{{$bt.Parent}}(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, parentid, id int) ({{$typename}}, error) {
	{{ if $cached }}//first attempt to retrieve from cache
	o,found := m.cache.Get(strconv.Itoa(id))
	if found {
		return o.({{$typename}}), nil
	}
	// fallback to database if not found{{ end }}
	var obj {{$typename}}

	err := m.db{{ if $dynamictable }}.Table(tableName){{ end }}.Scopes({{$typename}}FilterBy{{$bt.Parent}}(parentid, &m.db)).Find(&obj, id).Error
	{{ if $cached }} go m.cache.Set(strconv.Itoa(id), obj, cache.DefaultExpiration) {{ end }}
	return obj, err
}
{{end}}

func New{{$typename}}DB(db gorm.DB) *{{$typename}}DB {
	{{ if $cached }}
	return &{{$typename}}DB{
		db: db,
		cache: cache.New(5*time.Minute, 30*time.Second),
	}
	{{ else  }}
	return &{{$typename}}DB{db: db}

	{{ end  }}
}

func (m *{{$typename}}DB) DB() interface{} {
	return &m.db
}

func (m *{{$typename}}DB) List(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}) []{{$typename}} {

	var objs []{{$typename}}
	m.db{{ if $dynamictable }}.Table(tableName){{ end }}.Find(&objs)
	return objs
}

{{ range $idx, $col := columns .TypeDef.AttributeDefinition }}
func (m *{{$typename}}DB) ListBy{{title $col.Column}}Equal(ctx context.Context, {{lower $col.Column}} {{$col.Coltype}}{{ if $dynamictable }}, tableName string{{ end }}) []{{$typename}} {

	var objs []{{$typename}}
	m.db.Where("{{lower $col.Column}} = ?",  {{lower $col.Column}}){{ if $dynamictable }}.Table(tableName){{ end }}.Find(&objs)
	return objs
}
func (m *{{$typename}}DB) ListBy{{title $col.Column}}Like(ctx context.Context, {{lower $col.Column}} {{$col.Coltype}}{{ if $dynamictable }}, tableName string{{ end }}) []{{$typename}} {

	var objs []{{$typename}}
	m.db.Where("{{lower $col.Column}} like ?",  {{lower $col.Column}}){{ if $dynamictable }}.Table(tableName){{ end }}.Find(&objs)
	return objs
}
{{ end  }}

func (m *{{$typename}}DB) One(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, id int) ({{$typename}}, error) {
	{{ if $cached }}//first attempt to retrieve from cache
	o,found := m.cache.Get(strconv.Itoa(id))
	if found {
		return o.({{$typename}}), nil
	}
	// fallback to database if not found{{ end }}
	var obj {{$typename}}

	err := m.db{{ if $dynamictable }}.Table(tableName){{ end }}.Find(&obj, id).Error
	{{ if $cached }} go m.cache.Set(strconv.Itoa(id), obj, cache.DefaultExpiration) {{ end }}
	return obj, err
}

func (m *{{$typename}}DB) Add(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, model {{$typename}}) ({{$typename}}, error) {
	err := m.db{{ if $dynamictable }}.Table(tableName){{ end }}.Create(&model).Error
	{{ if $cached }} go m.cache.Set(strconv.Itoa(model.ID), model, cache.DefaultExpiration) {{ end }}
	return model, err
}

func (m *{{$typename}}DB) Update(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, model {{$typename}}) error {
	obj, err := m.One(ctx{{ if $dynamictable }}, tableName{{ end }}, model.ID)
	if err != nil {
		return  err
	}
	err = m.db{{ if $dynamictable }}.Table(tableName){{ end }}.Model(&obj).Updates(model).Error
	{{ if $cached }}
	go func(){
	obj, err := m.One(ctx, model.ID)
	if err == nil {
		m.cache.Set(strconv.Itoa(model.ID), obj, cache.DefaultExpiration)
	}
	}()
	{{ end }}

	return err
}

func (m *{{$typename}}DB) Delete(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, id int)  error {
	var obj {{$typename}}
	err := m.db{{ if $dynamictable }}.Table(tableName){{ end }}.Delete(&obj, id).Error
	if err != nil {
		return  err
	}
	{{ if $cached }} go m.cache.Delete(strconv.Itoa(id)) {{ end }}
	return  nil
}

{{ range $idx, $bt := .M2M}}
func (m *{{$typename}}DB) Delete{{$bt.Relation}}(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, {{lower $typename}}ID,  {{$bt.LowerRelation}}ID int)  error {
	var obj {{$typename}}
	obj.ID = {{lower $typename}}ID
	var assoc {{$bt.LowerRelation}}
	var err error
	assoc.ID = {{$bt.LowerRelation}}ID
	if err != nil {
		return err
	}
	err = m.db{{ if $dynamictable }}.Table(tableName){{ end }}.Model(&obj).Association("{{$bt.PluralRelation}}").Delete(assoc).Error
	if err != nil {
		return  err
	}
	return  nil
}
func (m *{{$typename}}DB) Add{{$bt.Relation}}(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, {{lower $typename}}ID, {{$bt.Relation}}ID int) error {
	var {{lower $typename}} {{$typename}}
	{{lower $typename}}.ID = {{lower $typename}}ID
	var assoc {{$bt.Relation}}
	assoc.ID = {{$bt.LowerRelation}}ID
	err := m.db{{ if $dynamictable }}.Table(tableName){{ end }}.Model(&{{lower $typename}}).Association("{{$bt.PluralRelation}}").Append(assoc).Error
	if err != nil {
		return  err
	}
	return  nil
}
func (m *{{$typename}}DB) List{{$bt.PluralRelation}}(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, {{lower $typename}}ID int)  []{{$bt.Relation}} {
	list := make([]{{$bt.Relation}}, 0)
	var obj {{$typename}}
	obj.ID = {{lower $typename}}ID
	m.db{{ if $dynamictable }}.Table(tableName){{ end }}.Model(&obj).Association("{{$bt.PluralRelation}}").Find(&list)
	return  nil
}
{{end}}
{{ range $idx, $bt := .BelongsTo}}
func Filter{{$typename}}By{{$bt.Parent}}(parent int, list []{{$typename}}) []{{$typename}} {
	filtered := make([]{{$typename}},0)
	for _,o := range list {
		if o.{{$bt.Parent}}ID == int(parent) {
			filtered = append(filtered,o)
		}
	}
	return filtered
}
{{end}}
`
<commit_msg>fix template<commit_after>package gorma

const modelTmpl = `// {{if .TypeDef.Description}}{{.TypeDef.Description}}{{else}}app.{{ .TypeName}} storage type{{end}}
// Identifier: {{ .TypeName}}
type {{.TypeName}} {{ modeldef .TypeDef }}
{{ $typedef := .TypeDef  }}
{{ $dynamictable := .DoDynamicTableName }}
{{ $typename  := .TypeName }}
{{ $cached := .DoCache }}
{{ if .DoCustomTableName }}
func (m {{$typename}}) TableName() string {
	return "{{ .CustomTableName}}"
}
{{ end }}
{{ if .DoRoler }}
func (m {{$typename}}) GetRole() string {
	return m.Role
}
{{end}}

type {{$typename}}Storage interface {
	DB() interface{}
	List(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}) []{{$typename}}
	One(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, id int) ({{$typename}}, error)
	Add(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, o {{$typename}}) ({{$typename}}, error)
	Update(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, o {{$typename}}) (error)
	Delete(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, id int) (error)
{{ range $idx, $bt := .BelongsTo}}
	ListBy{{$bt.Parent}}(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, parentid int) []{{$typename}}
	OneBy{{$bt.Parent}}(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, parentid, id int) ({{$typename}}, error)
{{end}}
	{{ storagedef $typedef }}
}
type {{$typename}}DB struct {
	db gorm.DB
	{{ if .DoCache }}cache *cache.Cache{{end}}
}
{{ range $idx, $bt := .BelongsTo}}
func {{$typename}}FilterBy{{$bt.Parent}}(parentid int, originaldb *gorm.DB) func(db *gorm.DB) *gorm.DB {
	if parentid > 0 {
		return func(db *gorm.DB) *gorm.DB {
			return db.Where("{{ $bt.DatabaseField }}_id = ?", parentid)
		}
	} else {
		return func(db *gorm.DB) *gorm.DB {
			return db
		}
	}
}

func (m *{{$typename}}DB) ListBy{{$bt.Parent}}(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, parentid int) []{{$typename}} {

	var objs []{{$typename}}
	m.db{{ if $dynamictable }}.Table(tableName){{ end }}.Scopes({{$typename}}FilterBy{{$bt.Parent}}(parentid, &m.db)).Find(&objs)
	return objs
}

func (m *{{$typename}}DB) OneBy{{$bt.Parent}}(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, parentid, id int) ({{$typename}}, error) {
	{{ if $cached }}//first attempt to retrieve from cache
	o,found := m.cache.Get(strconv.Itoa(id))
	if found {
		return o.({{$typename}}), nil
	}
	// fallback to database if not found{{ end }}
	var obj {{$typename}}

	err := m.db{{ if $dynamictable }}.Table(tableName){{ end }}.Scopes({{$typename}}FilterBy{{$bt.Parent}}(parentid, &m.db)).Find(&obj, id).Error
	{{ if $cached }} go m.cache.Set(strconv.Itoa(id), obj, cache.DefaultExpiration) {{ end }}
	return obj, err
}
{{end}}

func New{{$typename}}DB(db gorm.DB) *{{$typename}}DB {
	{{ if $cached }}
	return &{{$typename}}DB{
		db: db,
		cache: cache.New(5*time.Minute, 30*time.Second),
	}
	{{ else  }}
	return &{{$typename}}DB{db: db}

	{{ end  }}
}

func (m *{{$typename}}DB) DB() interface{} {
	return &m.db
}

func (m *{{$typename}}DB) List(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}) []{{$typename}} {

	var objs []{{$typename}}
	m.db{{ if $dynamictable }}.Table(tableName){{ end }}.Find(&objs)
	return objs
}

{{ range $idx, $col := columns .TypeDef.AttributeDefinition }}
func (m *{{$typename}}DB) ListBy{{title $col.Column}}Equal(ctx context.Context, {{lower $col.Column}} {{$col.Coltype}}{{ if $dynamictable }}, tableName string{{ end }}) []{{$typename}} {

	var objs []{{$typename}}
	m.db.Where("{{lower $col.Column}} = ?",  {{lower $col.Column}}){{ if $dynamictable }}.Table(tableName){{ end }}.Find(&objs)
	return objs
}
func (m *{{$typename}}DB) ListBy{{title $col.Column}}Like(ctx context.Context, {{lower $col.Column}} {{$col.Coltype}}{{ if $dynamictable }}, tableName string{{ end }}) []{{$typename}} {

	var objs []{{$typename}}
	m.db.Where("{{lower $col.Column}} like ?",  {{lower $col.Column}}){{ if $dynamictable }}.Table(tableName){{ end }}.Find(&objs)
	return objs
}
{{ end  }}

func (m *{{$typename}}DB) One(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, id int) ({{$typename}}, error) {
	{{ if $cached }}//first attempt to retrieve from cache
	o,found := m.cache.Get(strconv.Itoa(id))
	if found {
		return o.({{$typename}}), nil
	}
	// fallback to database if not found{{ end }}
	var obj {{$typename}}

	err := m.db{{ if $dynamictable }}.Table(tableName){{ end }}.Find(&obj, id).Error
	{{ if $cached }} go m.cache.Set(strconv.Itoa(id), obj, cache.DefaultExpiration) {{ end }}
	return obj, err
}

func (m *{{$typename}}DB) Add(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, model {{$typename}}) ({{$typename}}, error) {
	err := m.db{{ if $dynamictable }}.Table(tableName){{ end }}.Create(&model).Error
	{{ if $cached }} go m.cache.Set(strconv.Itoa(model.ID), model, cache.DefaultExpiration) {{ end }}
	return model, err
}

func (m *{{$typename}}DB) Update(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, model {{$typename}}) error {
	obj, err := m.One(ctx{{ if $dynamictable }}, tableName{{ end }}, model.ID)
	if err != nil {
		return  err
	}
	err = m.db{{ if $dynamictable }}.Table(tableName){{ end }}.Model(&obj).Updates(model).Error
	{{ if $cached }}
	go func(){
	obj, err := m.One(ctx, model.ID)
	if err == nil {
		m.cache.Set(strconv.Itoa(model.ID), obj, cache.DefaultExpiration)
	}
	}()
	{{ end }}

	return err
}

func (m *{{$typename}}DB) Delete(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, id int)  error {
	var obj {{$typename}}
	err := m.db{{ if $dynamictable }}.Table(tableName){{ end }}.Delete(&obj, id).Error
	if err != nil {
		return  err
	}
	{{ if $cached }} go m.cache.Delete(strconv.Itoa(id)) {{ end }}
	return  nil
}

{{ range $idx, $bt := .M2M}}
func (m *{{$typename}}DB) Delete{{$bt.Relation}}(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, {{lower $typename}}ID,  {{$bt.LowerRelation}}ID int)  error {
	var obj {{$typename}}
	obj.ID = {{lower $typename}}ID
	var assoc {{$bt.Relation}}
	var err error
	assoc.ID = {{$bt.LowerRelation}}ID
	if err != nil {
		return err
	}
	err = m.db{{ if $dynamictable }}.Table(tableName){{ end }}.Model(&obj).Association("{{$bt.PluralRelation}}").Delete(assoc).Error
	if err != nil {
		return  err
	}
	return  nil
}
func (m *{{$typename}}DB) Add{{$bt.Relation}}(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, {{lower $typename}}ID, {{$bt.LowerRelation}}ID int) error {
	var {{lower $typename}} {{$typename}}
	{{lower $typename}}.ID = {{lower $typename}}ID
	var assoc {{$bt.Relation}}
	assoc.ID = {{$bt.LowerRelation}}ID
	err := m.db{{ if $dynamictable }}.Table(tableName){{ end }}.Model(&{{lower $typename}}).Association("{{$bt.PluralRelation}}").Append(assoc).Error
	if err != nil {
		return  err
	}
	return  nil
}
func (m *{{$typename}}DB) List{{$bt.PluralRelation}}(ctx context.Context{{ if $dynamictable }}, tableName string{{ end }}, {{lower $typename}}ID int)  []{{$bt.Relation}} {
	list := make([]{{$bt.Relation}}, 0)
	var obj {{$typename}}
	obj.ID = {{lower $typename}}ID
	m.db{{ if $dynamictable }}.Table(tableName){{ end }}.Model(&obj).Association("{{$bt.PluralRelation}}").Find(&list)
	return  nil
}
{{end}}
{{ range $idx, $bt := .BelongsTo}}
func Filter{{$typename}}By{{$bt.Parent}}(parent int, list []{{$typename}}) []{{$typename}} {
	filtered := make([]{{$typename}},0)
	for _,o := range list {
		if o.{{$bt.Parent}}ID == int(parent) {
			filtered = append(filtered,o)
		}
	}
	return filtered
}
{{end}}
`
<|endoftext|>


<commit_before>package sloth

import (
  "log"
  "bytes"
  "net/http"
  "net/url"
  "database/sql"
  // "crypto/hmac"
  _ "github.com/go-sql-driver/mysql"
)

// Hooks

var _ RestfulHook = (*RestHook)(nil)

type RestfulHook interface {
  Mesg(interface{}) (*http.Response, error)
  Kill() (sql.Result, error)
}

type RestHook struct {
  id int
  resourceSlug     string
  subscriberUrl    string
  subscriberMethod string
}

func (hook *RestHook) Url() string {
  return hook.subscriberUrl
}

func (hook *RestHook) Method() string {
  return hook.subscriberMethod
}

func (hook *RestHook) Mesg(data interface{}) (*http.Response, error) {
  hookCaller   := &http.Client{}
  dataBytes, _ := AsBytes(data)

  req, err := http.NewRequest(hook.subscriberMethod, hook.subscriberUrl, bytes.NewBuffer(dataBytes))

  if err != nil {
    panic(err)
  }

  req.Header.Add("X-Sloth-Hook-Id",        `W/"something"`)
  req.Header.Add("X-Sloth-Hook-Signature", `W/"abc123"`) // TODO - HMAC

  resp, err := hookCaller.Do(req)

  defer resp.Body.Close()

  return resp, err
}

func (hook *RestHook) Kill() (sql.Result, error) {
  return new(HookRepo).Db().Exec("delete from hooks where resource_slug = ? and subscriber_url = ? and subscriber_method = ?", hook.subscriberUrl, hook.subscriberMethod)
}

// Hook resource

var _ HookableResource = (*HookResource)(nil)

type HookableResource interface {
  RestfulResource

  Hooks() []RestHook
  Subscribe(subUrl string, subMethod string)
  Broadcast(data interface{})
}

type HookResource struct {
  UrlSlug string
  RestResource
}

func (*HookResource) Type() string {
  return "text/html"
}

func (resource *HookResource) Hooks() []RestHook {
  var parsedHooks []RestHook

  storedHooks, err := new(HookRepo).ForResource(resource)

  defer storedHooks.Close()

  for storedHooks.Next() {
    var id  int
    var subscriberUrl    string
    var subscriberMethod string

    err := storedHooks.Scan(&id, &subscriberUrl, &subscriberMethod)

    if err != nil {
      panic("Failed to parse RestHook from repository")
    }

    newHook := RestHook{resourceSlug: resource.Slug(), subscriberUrl: subscriberUrl, subscriberMethod: subscriberMethod}

    parsedHooks = append(parsedHooks, newHook)
  }

  err = storedHooks.Err()

  if err != nil {
    panic("Failed to acquire RestHooks from repository")
  }

  return parsedHooks
}

func (resource *HookResource) Slug() string {
  return resource.UrlSlug
}

// FIXME
func (resource *HookResource) Put(values url.Values) (int, interface{}) {
  subscriberUrl, subUrlOk       := values["subscriber_url"]
  subscriberMethod, subMethodOk := values["subscriber_method"]

  if !subUrlOk || !subMethodOk {
    return 400, "Missing subscriber_url and/or subscriber_method"
  }

  switch subscriberMethod[0] {
    case GET, POST, PUT, PATCH, DELETE: resource.Subscribe(subscriberUrl[0], subscriberMethod[0])
    default: return 400, "Unsupported subscriber_method"
  }

   return 204, ""
}

func (resource *HookResource) Subscribe(subUrl string, subMethod string) {
  newHook := RestHook {
    subscriberUrl    : subUrl,
    subscriberMethod : subMethod,
  }

  new(HookRepo).Add(&newHook)

  log.Printf("Successful subscription (subUrl: %s, subMethod: %s)", subUrl, subMethod)
}

func (resource *HookResource) Broadcast(data interface{}) {
  for _, hook := range resource.Hooks() {
    go func() { // WARN - this can get a bit crazy if we have thousands of subscribers. need to make this scale properly
      hook.Mesg(data)
    }()
  }
}

// Hook repository

type HookRepo struct { }

func (repo *HookRepo) Db() *sql.DB {
  db, err := sql.Open("mysql", "user:password@/hooks") // FIXME - integrate with config (https://code.google.com/p/gcfg/)

  if err != nil {
    log.Fatal("Failed to instantiate hook repository", err)
  }

  return db
}

func (repo *HookRepo) All() (*sql.Rows, error) {
  return repo.Db().Query("select id, resource_slug, subscriber_url, subscriber_method from hooks")
}

func (repo *HookRepo) ForResource(resource *HookResource) (*sql.Rows, error) {
  return repo.Db().Query("select id, subscriber_url, subscriber_method from hooks where resource_slug = ?", resource.Slug())
}

func (repo *HookRepo) Add(hook *RestHook) (sql.Result, error) {
  return repo.Db().Exec("insert into hooks (resource_slug, subscriber_url, subscriber_method) values (?, ?)", hook.resourceSlug, hook.subscriberUrl, hook.subscriberMethod)
}

func (repo *HookRepo) Delete(hook *RestHook) (sql.Result, error) {
  return repo.Db().Exec("delete from hooks where resource_slug = ? and subscriber_url = ? and subscriber_method = ?", hook.resourceSlug, hook.subscriberUrl, hook.subscriberMethod)
}

func (repo *HookRepo) Close() {
  repo.Db().Close()
}
<commit_msg>headers for hook pub mesg<commit_after>package sloth

import (
  "log"
  "bytes"
  "net/http"
  "net/url"
  "database/sql"
  // "crypto/hmac"
  _ "github.com/go-sql-driver/mysql"
)

// Hooks

var _ RestfulHook = (*RestHook)(nil)

type RestfulHook interface {
  Mesg(interface{}) (*http.Response, error)
  Kill() (sql.Result, error)
}

type RestHook struct {
  id string
  resourceSlug     string
  subscriberUrl    string
  subscriberMethod string
}

func (hook *RestHook) Url() string {
  return hook.subscriberUrl
}

func (hook *RestHook) Method() string {
  return hook.subscriberMethod
}

func (hook *RestHook) Mesg(data interface{}) (*http.Response, error) {
  hookCaller   := &http.Client{}
  dataBytes, _ := AsBytes(data)

  req, err := http.NewRequest(hook.subscriberMethod, hook.subscriberUrl, bytes.NewBuffer(dataBytes))

  if err != nil {
    panic(err)
  }

  req.Header.Add("X-Sloth-Hook-Id",        `W/"` + hook.id + `"`)
  req.Header.Add("X-Sloth-Hook-Signature", `W/"abc123"`) // TODO - HMAC

  resp, err := hookCaller.Do(req)

  defer resp.Body.Close()

  return resp, err
}

func (hook *RestHook) Kill() (sql.Result, error) {
  return new(HookRepo).Db().Exec("delete from hooks where resource_slug = ? and subscriber_url = ? and subscriber_method = ?", hook.subscriberUrl, hook.subscriberMethod)
}

// Hook resource

var _ HookableResource = (*HookResource)(nil)

type HookableResource interface {
  RestfulResource

  Hooks() []RestHook
  Subscribe(subUrl string, subMethod string)
  Broadcast(data interface{})
}

type HookResource struct {
  UrlSlug string
  RestResource
}

func (*HookResource) Type() string {
  return "text/html"
}

func (resource *HookResource) Hooks() []RestHook {
  var parsedHooks []RestHook

  storedHooks, err := new(HookRepo).ForResource(resource)

  defer storedHooks.Close()

  for storedHooks.Next() {
    var id  int
    var subscriberUrl    string
    var subscriberMethod string

    err := storedHooks.Scan(&id, &subscriberUrl, &subscriberMethod)

    if err != nil {
      panic("Failed to parse RestHook from repository")
    }

    newHook := RestHook{resourceSlug: resource.Slug(), subscriberUrl: subscriberUrl, subscriberMethod: subscriberMethod}

    parsedHooks = append(parsedHooks, newHook)
  }

  err = storedHooks.Err()

  if err != nil {
    panic("Failed to acquire RestHooks from repository")
  }

  return parsedHooks
}

func (resource *HookResource) Slug() string {
  return resource.UrlSlug
}

// FIXME
func (resource *HookResource) Put(values url.Values) (int, interface{}) {
  subscriberUrl, subUrlOk       := values["subscriber_url"]
  subscriberMethod, subMethodOk := values["subscriber_method"]

  if !subUrlOk || !subMethodOk {
    return 400, "Missing subscriber_url and/or subscriber_method"
  }

  switch subscriberMethod[0] {
    case GET, POST, PUT, PATCH, DELETE: resource.Subscribe(subscriberUrl[0], subscriberMethod[0])
    default: return 400, "Unsupported subscriber_method"
  }

   return 204, ""
}

func (resource *HookResource) Subscribe(subUrl string, subMethod string) {
  newHook := RestHook {
    subscriberUrl    : subUrl,
    subscriberMethod : subMethod,
  }

  new(HookRepo).Add(&newHook)

  log.Printf("Successful subscription (subUrl: %s, subMethod: %s)", subUrl, subMethod)
}

func (resource *HookResource) Broadcast(data interface{}) {
  for _, hook := range resource.Hooks() {
    go func() { // WARN - this can get a bit crazy if we have thousands of subscribers. need to make this scale properly
      hook.Mesg(data)
    }()
  }
}

// Hook repository

type HookRepo struct { }

func (repo *HookRepo) Db() *sql.DB {
  db, err := sql.Open("mysql", "user:password@/hooks") // FIXME - integrate with config (https://code.google.com/p/gcfg/)

  if err != nil {
    log.Fatal("Failed to instantiate hook repository", err)
  }

  return db
}

func (repo *HookRepo) All() (*sql.Rows, error) {
  return repo.Db().Query("select id, resource_slug, subscriber_url, subscriber_method from hooks")
}

func (repo *HookRepo) ForResource(resource *HookResource) (*sql.Rows, error) {
  return repo.Db().Query("select id, subscriber_url, subscriber_method from hooks where resource_slug = ?", resource.Slug())
}

func (repo *HookRepo) Add(hook *RestHook) (sql.Result, error) {
  return repo.Db().Exec("insert into hooks (resource_slug, subscriber_url, subscriber_method) values (?, ?)", hook.resourceSlug, hook.subscriberUrl, hook.subscriberMethod)
}

func (repo *HookRepo) Delete(hook *RestHook) (sql.Result, error) {
  return repo.Db().Exec("delete from hooks where resource_slug = ? and subscriber_url = ? and subscriber_method = ?", hook.resourceSlug, hook.subscriberUrl, hook.subscriberMethod)
}

func (repo *HookRepo) Close() {
  repo.Db().Close()
}
<|endoftext|>


<commit_before>//
// markup.go
// memeposting markup parser
//
package srnd

import (
  "html"
  "strings"
)

func memeposting(src string) string {
  // escape
  src = html.EscapeString(src)
  // make newlines
  found_code_tag := false
  code_content := ""
  markup := ""
  for _, line := range strings.Split(src, "\n") {
    if strings.Count(line, "[code]") == 1 {
      found_code_tag = true
      code_content = strings.Split(line, "[code]")[0]
    } else if found_code_tag {
      code_content += line + "\n"
      if strings.Count(line, "[/code]") == 1 {
        found_code_tag = false
        markup += "
"
        code_content = strings.Replace(code_content, "[/code]", "", -1)
        for _, code_line := range strings.Split(code_content, "\n") {
          markup += "
"
          markup += code_line
          markup += "
"
        }
        markup += "
"
        code_content = ""
      } else {
        continue
      }
    } else {
      // check for meme arrows
      if strings.HasPrefix(line, ">") {
        markup += "
"
        markup += line
        markup += "
"
      } else {
        markup += "
"
        markup += line
        markup += "
"
      }
    }
  }

  // return
  return markup
}
<commit_msg>fix/rebase code tag code<commit_after>//
// markup.go
// memeposting markup parser
//
package srnd

import (
  "html"
  "strings"
)
// meme arrow a line
func memeline(line string) (markup string) {
  // check for meme arrows
  if strings.HasPrefix(line, ">") {
    markup += "
"
    markup += line
    markup += "
"
  } else {
    markup += "
"
    markup += line
    markup += "
"
  }
  return
}

func memeposting(src string) (markup string) {
  // escape
  src = html.EscapeString(src)

  found_code_tag := false
  code_content := ""
  // for each line...
  for _, line := range strings.Split(src, "\n") {
    // beginning of code tag ?
    if strings.Count(line, "[code]") > 0 {
      // yes there's a code tag
      found_code_tag = true
    }
    if found_code_tag {
      // collect content of code tag
      code_content += line + "\n"
      // end of code tag ?
      if strings.Count(line, "[/code]") == 1 {
        // yah
        found_code_tag = false
        // open pre tag
        markup += "
"
        // remove open tag, only once so we can have a code tag verbatum inside
        code_content = strings.Replace(code_content, "[code]", "", 1)
        // remove all close tags, should only have 1
        code_content = strings.Replace(code_content, "[/code]", "", -1)
        // make into lines
        for _, code_line := range strings.Split(code_content, "\n") {
          //TODO: This also memearrows, should we?
          markup += memeline(code_line)
        }
        // close pre tag
        markup += "
"
        // reset content buffer
        code_content = ""
      }
      // next line
      continue
    }
    // format line regularlly
    markup += memeline(line)
  }
  // flush the rest of an incomplete code tag
  for _, line := range strings.Split(code_content, "\n") {
    markup += memeline(line)
  }
  return
}
<|endoftext|>


<commit_before>// Copyright 2014 The Cockroach Authors.
//
// 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. See the AUTHORS file
// for names of contributors.
//
// Author: Ben Darnell

package multiraft

import (
	"reflect"
	"testing"
	"time"

	"github.com/cockroachdb/cockroach/util"
	"github.com/cockroachdb/cockroach/util/leaktest"
	"github.com/cockroachdb/cockroach/util/log"
	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
	"github.com/coreos/etcd/raft"
	"github.com/coreos/etcd/raft/raftpb"
)

var testRand, _ = util.NewPseudoRand()

func makeCommandID() string {
	return string(util.RandBytes(testRand, commandIDLen))
}

type testCluster struct {
	t         *testing.T
	nodes     []*state
	tickers   []*manualTicker
	events    []*eventDemux
	storages  []*BlockableStorage
	transport Transport
}

func newTestCluster(transport Transport, size int, t *testing.T) *testCluster {
	if transport == nil {
		transport = NewLocalRPCTransport()
	}
	cluster := &testCluster{t: t}
	cluster.transport = transport
	for i := 0; i < size; i++ {
		ticker := newManualTicker()
		storage := &BlockableStorage{storage: NewMemoryStorage()}
		config := &Config{
			Transport:              transport,
			Storage:                storage,
			Ticker:                 ticker,
			ElectionTimeoutTicks:   2,
			HeartbeatIntervalTicks: 1,
			TickInterval:           time.Hour, // not in use
			Strict:                 true,
		}
		mr, err := NewMultiRaft(NodeID(i+1), config)
		if err != nil {
			t.Fatal(err)
		}
		state := newState(mr)
		demux := newEventDemux(state.Events)
		demux.start()
		cluster.nodes = append(cluster.nodes, state)
		cluster.tickers = append(cluster.tickers, ticker)
		cluster.events = append(cluster.events, demux)
		cluster.storages = append(cluster.storages, storage)
	}
	cluster.start()
	return cluster
}

func (c *testCluster) start() {
	// Let all the states listen before starting any.
	for _, node := range c.nodes {
		go node.start()
	}
}

func (c *testCluster) stop() {
	for _, node := range c.nodes {
		node.Stop()
	}
	for _, demux := range c.events {
		demux.stop()
	}
	c.transport.Close()
}

// createGroup replicates a group consisting of numReplicas members,
// the first being the node at index firstNode.
func (c *testCluster) createGroup(groupID uint64, firstNode, numReplicas int) {
	var replicaIDs []uint64
	for i := 0; i < numReplicas; i++ {
		replicaIDs = append(replicaIDs, uint64(c.nodes[firstNode+i].nodeID))
	}
	for i := 0; i < numReplicas; i++ {
		gs := c.storages[firstNode+i].GroupStorage(groupID)
		memStorage := gs.(*blockableGroupStorage).s.(*raft.MemoryStorage)
		if err := memStorage.SetHardState(raftpb.HardState{
			Commit: 10,
			Term:   5,
		}); err != nil {
			c.t.Fatal(err)
		}
		if err := memStorage.ApplySnapshot(raftpb.Snapshot{
			Metadata: raftpb.SnapshotMetadata{
				ConfState: raftpb.ConfState{
					Nodes: replicaIDs,
				},
				Index: 10,
				Term:  5,
			},
		}); err != nil {
			c.t.Fatal(err)
		}

		node := c.nodes[firstNode+i]
		err := node.CreateGroup(groupID)
		if err != nil {
			c.t.Fatal(err)
		}
	}
}

func (c *testCluster) triggerElection(nodeIndex int, groupID uint64) {
	if err := c.nodes[nodeIndex].multiNode.Campaign(context.Background(), groupID); err != nil {
		c.t.Fatal(err)
	}
}

// Trigger an election on node i and wait for it to complete.
// TODO(bdarnell): once we have better leader discovery and forwarding/queuing, remove this.
func (c *testCluster) waitForElection(i int) *EventLeaderElection {
	for {
		e := <-c.events[i].LeaderElection
		if e == nil {
			panic("got nil LeaderElection event, channel likely closed")
		}
		// Ignore events with NodeID 0; these mark elections that are in progress.
		if e.NodeID != 0 {
			return e
		}
	}
}

func TestInitialLeaderElection(t *testing.T) {
	defer leaktest.AfterTest(t)
	// Run the test three times, each time triggering a different node's election clock.
	// The node that requests an election first should win.
	for leaderIndex := 0; leaderIndex < 3; leaderIndex++ {
		log.Infof("testing leader election for node %v", leaderIndex)
		cluster := newTestCluster(nil, 3, t)
		groupID := uint64(1)
		cluster.createGroup(groupID, 0, 3)

		cluster.triggerElection(leaderIndex, groupID)
		event := cluster.waitForElection(leaderIndex)
		if event.GroupID != groupID {
			t.Fatalf("election event had incorrect group id %v", event.GroupID)
		}
		if event.NodeID != cluster.nodes[leaderIndex].nodeID {
			t.Fatalf("expected %v to win election, but was %v", cluster.nodes[leaderIndex].nodeID,
				event.NodeID)
		}
		cluster.stop()
	}
}

func TestLeaderElectionEvent(t *testing.T) {
	defer leaktest.AfterTest(t)
	// Leader election events are fired when the leader commits an entry, not when it
	// issues a call for votes.
	cluster := newTestCluster(nil, 3, t)
	defer cluster.stop()
	groupID := uint64(1)
	cluster.createGroup(groupID, 0, 3)

	// Send a Ready with a new leader but no new commits.
	// This happens while an election is in progress.
	cluster.nodes[1].handleRaftReady(map[uint64]raft.Ready{
		groupID: {
			SoftState: &raft.SoftState{
				Lead: 3,
			},
		},
	})

	// No events are sent.
	select {
	case e := <-cluster.events[1].LeaderElection:
		t.Fatalf("got unexpected event %v", e)
	case <-time.After(time.Millisecond):
	}

	// Now there are new committed entries. A new leader always commits an entry
	// to conclude the election.
	entry := raftpb.Entry{
		Index: 42,
		Term:  42,
	}
	cluster.nodes[1].handleRaftReady(map[uint64]raft.Ready{
		groupID: {
			Entries:          []raftpb.Entry{entry},
			CommittedEntries: []raftpb.Entry{entry},
		},
	})

	// Now we get an event.
	select {
	case e := <-cluster.events[1].LeaderElection:
		if !reflect.DeepEqual(e, &EventLeaderElection{
			GroupID: groupID,
			NodeID:  3,
			Term:    42,
		}) {
			t.Errorf("election event did not match expectations: %+v", e)
		}
	case <-time.After(time.Millisecond):
		t.Fatal("didn't get expected event")
	}
}

func TestCommand(t *testing.T) {
	defer leaktest.AfterTest(t)
	cluster := newTestCluster(nil, 3, t)
	defer cluster.stop()
	groupID := uint64(1)
	cluster.createGroup(groupID, 0, 3)
	cluster.triggerElection(0, groupID)
	cluster.waitForElection(0)

	// Submit a command to the leader
	cluster.nodes[0].SubmitCommand(groupID, makeCommandID(), []byte("command"))

	// The command will be committed on each node.
	for i, events := range cluster.events {
		log.Infof("waiting for event to be committed on node %v", i)
		commit := <-events.CommandCommitted
		if string(commit.Command) != "command" {
			t.Errorf("unexpected value in committed command: %v", commit.Command)
		}
	}
}

func TestSlowStorage(t *testing.T) {
	defer leaktest.AfterTest(t)
	cluster := newTestCluster(nil, 3, t)
	defer cluster.stop()
	groupID := uint64(1)
	cluster.createGroup(groupID, 0, 3)

	cluster.triggerElection(0, groupID)
	cluster.waitForElection(0)

	// Block the storage on the last node.
	// TODO(bdarnell): there appear to still be issues if the storage is blocked during
	// the election.
	cluster.storages[2].Block()

	// Submit a command to the leader
	cluster.nodes[0].SubmitCommand(groupID, makeCommandID(), []byte("command"))

	// Even with the third node blocked, the other nodes can make progress.
	for i := 0; i < 2; i++ {
		events := cluster.events[i]
		log.Infof("waiting for event to be commited on node %v", i)
		commit := <-events.CommandCommitted
		if string(commit.Command) != "command" {
			t.Errorf("unexpected value in committed command: %v", commit.Command)
		}
	}

	// Ensure that node 2 is in fact blocked.
	time.Sleep(time.Millisecond)
	select {
	case commit := <-cluster.events[2].CommandCommitted:
		t.Errorf("didn't expect commits on node 2 but got %v", commit)
	default:
	}

	// After unblocking the third node, it will catch up.
	cluster.storages[2].Unblock()
	cluster.tickers[0].Tick()
	log.Infof("waiting for event to be commited on node 2")
	commit := <-cluster.events[2].CommandCommitted
	if string(commit.Command) != "command" {
		t.Errorf("unexpected value in committed command: %v", commit.Command)
	}
}

func TestMembershipChange(t *testing.T) {
	defer leaktest.AfterTest(t)
	cluster := newTestCluster(nil, 4, t)
	defer cluster.stop()

	// Create a group with a single member, cluster.nodes[0].
	groupID := uint64(1)
	cluster.createGroup(groupID, 0, 1)
	cluster.triggerElection(0, groupID)
	cluster.waitForElection(0)

	// Consume and apply the membership change events.
	for i := 0; i < 4; i++ {
		go func(i int) {
			for {
				e, ok := <-cluster.events[i].MembershipChangeCommitted
				if !ok {
					return
				}
				e.Callback(nil)
			}
		}(i)
	}

	// Add each of the other three nodes to the cluster.
	for i := 1; i < 4; i++ {
		ch := cluster.nodes[0].ChangeGroupMembership(groupID, makeCommandID(),
			raftpb.ConfChangeAddNode,
			cluster.nodes[i].nodeID, nil)
		<-ch
	}

	// TODO(bdarnell): verify that the channel events are sent out correctly.
	/*
		for i := 0; i < 10; i++ {
			log.Infof("tick %d", i)
			cluster.tickers[0].Tick()
			time.Sleep(5 * time.Millisecond)
		}

		// Each node is notified of each other node's joining.
		for i := 0; i < 4; i++ {
			for j := 1; j < 4; j++ {
				select {
				case e := <-cluster.events[i].MembershipChangeCommitted:
					if e.NodeID != cluster.nodes[j].nodeID {
						t.Errorf("node %d expected event for %d, got %d", i, j, e.NodeID)
					}
				default:
					t.Errorf("node %d did not get expected event for %d", i, j)
				}
			}
		}*/
}
<commit_msg>Add a test for propose error.<commit_after>// Copyright 2014 The Cockroach Authors.
//
// 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. See the AUTHORS file
// for names of contributors.
//
// Author: Ben Darnell

package multiraft

import (
	"reflect"
	"testing"
	"time"

	"github.com/cockroachdb/cockroach/util"
	"github.com/cockroachdb/cockroach/util/leaktest"
	"github.com/cockroachdb/cockroach/util/log"
	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
	"github.com/coreos/etcd/raft"
	"github.com/coreos/etcd/raft/raftpb"
)

var testRand, _ = util.NewPseudoRand()

func makeCommandID() string {
	return string(util.RandBytes(testRand, commandIDLen))
}

type testCluster struct {
	t         *testing.T
	nodes     []*state
	tickers   []*manualTicker
	events    []*eventDemux
	storages  []*BlockableStorage
	transport Transport
}

func newTestCluster(transport Transport, size int, t *testing.T) *testCluster {
	if transport == nil {
		transport = NewLocalRPCTransport()
	}
	cluster := &testCluster{t: t}
	cluster.transport = transport
	for i := 0; i < size; i++ {
		ticker := newManualTicker()
		storage := &BlockableStorage{storage: NewMemoryStorage()}
		config := &Config{
			Transport:              transport,
			Storage:                storage,
			Ticker:                 ticker,
			ElectionTimeoutTicks:   2,
			HeartbeatIntervalTicks: 1,
			TickInterval:           time.Hour, // not in use
			Strict:                 true,
		}
		mr, err := NewMultiRaft(NodeID(i+1), config)
		if err != nil {
			t.Fatal(err)
		}
		state := newState(mr)
		demux := newEventDemux(state.Events)
		demux.start()
		cluster.nodes = append(cluster.nodes, state)
		cluster.tickers = append(cluster.tickers, ticker)
		cluster.events = append(cluster.events, demux)
		cluster.storages = append(cluster.storages, storage)
	}
	cluster.start()
	return cluster
}

func (c *testCluster) start() {
	// Let all the states listen before starting any.
	for _, node := range c.nodes {
		go node.start()
	}
}

func (c *testCluster) stop() {
	for _, node := range c.nodes {
		node.Stop()
	}
	for _, demux := range c.events {
		demux.stop()
	}
	c.transport.Close()
}

// createGroup replicates a group consisting of numReplicas members,
// the first being the node at index firstNode.
func (c *testCluster) createGroup(groupID uint64, firstNode, numReplicas int) {
	var replicaIDs []uint64
	for i := 0; i < numReplicas; i++ {
		replicaIDs = append(replicaIDs, uint64(c.nodes[firstNode+i].nodeID))
	}
	for i := 0; i < numReplicas; i++ {
		gs := c.storages[firstNode+i].GroupStorage(groupID)
		memStorage := gs.(*blockableGroupStorage).s.(*raft.MemoryStorage)
		if err := memStorage.SetHardState(raftpb.HardState{
			Commit: 10,
			Term:   5,
		}); err != nil {
			c.t.Fatal(err)
		}
		if err := memStorage.ApplySnapshot(raftpb.Snapshot{
			Metadata: raftpb.SnapshotMetadata{
				ConfState: raftpb.ConfState{
					Nodes: replicaIDs,
				},
				Index: 10,
				Term:  5,
			},
		}); err != nil {
			c.t.Fatal(err)
		}

		node := c.nodes[firstNode+i]
		err := node.CreateGroup(groupID)
		if err != nil {
			c.t.Fatal(err)
		}
	}
}

func (c *testCluster) triggerElection(nodeIndex int, groupID uint64) {
	if err := c.nodes[nodeIndex].multiNode.Campaign(context.Background(), groupID); err != nil {
		c.t.Fatal(err)
	}
}

// Trigger an election on node i and wait for it to complete.
// TODO(bdarnell): once we have better leader discovery and forwarding/queuing, remove this.
func (c *testCluster) waitForElection(i int) *EventLeaderElection {
	for {
		e := <-c.events[i].LeaderElection
		if e == nil {
			panic("got nil LeaderElection event, channel likely closed")
		}
		// Ignore events with NodeID 0; these mark elections that are in progress.
		if e.NodeID != 0 {
			return e
		}
	}
}

func TestInitialLeaderElection(t *testing.T) {
	defer leaktest.AfterTest(t)
	// Run the test three times, each time triggering a different node's election clock.
	// The node that requests an election first should win.
	for leaderIndex := 0; leaderIndex < 3; leaderIndex++ {
		log.Infof("testing leader election for node %v", leaderIndex)
		cluster := newTestCluster(nil, 3, t)
		groupID := uint64(1)
		cluster.createGroup(groupID, 0, 3)

		cluster.triggerElection(leaderIndex, groupID)
		event := cluster.waitForElection(leaderIndex)
		if event.GroupID != groupID {
			t.Fatalf("election event had incorrect group id %v", event.GroupID)
		}
		if event.NodeID != cluster.nodes[leaderIndex].nodeID {
			t.Fatalf("expected %v to win election, but was %v", cluster.nodes[leaderIndex].nodeID,
				event.NodeID)
		}
		cluster.stop()
	}
}

// TestProposeBadGroup ensures that unknown group IDs are an error, not a panic.
func TestProposeBadGroup(t *testing.T) {
	defer leaktest.AfterTest(t)
	cluster := newTestCluster(nil, 3, t)
	defer cluster.stop()
	err := <-cluster.nodes[1].SubmitCommand(7, "asdf", []byte{})
	if err == nil {
		t.Fatal("did not get expected error")
	}
}

func TestLeaderElectionEvent(t *testing.T) {
	defer leaktest.AfterTest(t)
	// Leader election events are fired when the leader commits an entry, not when it
	// issues a call for votes.
	cluster := newTestCluster(nil, 3, t)
	defer cluster.stop()
	groupID := uint64(1)
	cluster.createGroup(groupID, 0, 3)

	// Send a Ready with a new leader but no new commits.
	// This happens while an election is in progress.
	cluster.nodes[1].handleRaftReady(map[uint64]raft.Ready{
		groupID: {
			SoftState: &raft.SoftState{
				Lead: 3,
			},
		},
	})

	// No events are sent.
	select {
	case e := <-cluster.events[1].LeaderElection:
		t.Fatalf("got unexpected event %v", e)
	case <-time.After(time.Millisecond):
	}

	// Now there are new committed entries. A new leader always commits an entry
	// to conclude the election.
	entry := raftpb.Entry{
		Index: 42,
		Term:  42,
	}
	cluster.nodes[1].handleRaftReady(map[uint64]raft.Ready{
		groupID: {
			Entries:          []raftpb.Entry{entry},
			CommittedEntries: []raftpb.Entry{entry},
		},
	})

	// Now we get an event.
	select {
	case e := <-cluster.events[1].LeaderElection:
		if !reflect.DeepEqual(e, &EventLeaderElection{
			GroupID: groupID,
			NodeID:  3,
			Term:    42,
		}) {
			t.Errorf("election event did not match expectations: %+v", e)
		}
	case <-time.After(time.Millisecond):
		t.Fatal("didn't get expected event")
	}
}

func TestCommand(t *testing.T) {
	defer leaktest.AfterTest(t)
	cluster := newTestCluster(nil, 3, t)
	defer cluster.stop()
	groupID := uint64(1)
	cluster.createGroup(groupID, 0, 3)
	cluster.triggerElection(0, groupID)
	cluster.waitForElection(0)

	// Submit a command to the leader
	cluster.nodes[0].SubmitCommand(groupID, makeCommandID(), []byte("command"))

	// The command will be committed on each node.
	for i, events := range cluster.events {
		log.Infof("waiting for event to be committed on node %v", i)
		commit := <-events.CommandCommitted
		if string(commit.Command) != "command" {
			t.Errorf("unexpected value in committed command: %v", commit.Command)
		}
	}
}

func TestSlowStorage(t *testing.T) {
	defer leaktest.AfterTest(t)
	cluster := newTestCluster(nil, 3, t)
	defer cluster.stop()
	groupID := uint64(1)
	cluster.createGroup(groupID, 0, 3)

	cluster.triggerElection(0, groupID)
	cluster.waitForElection(0)

	// Block the storage on the last node.
	// TODO(bdarnell): there appear to still be issues if the storage is blocked during
	// the election.
	cluster.storages[2].Block()

	// Submit a command to the leader
	cluster.nodes[0].SubmitCommand(groupID, makeCommandID(), []byte("command"))

	// Even with the third node blocked, the other nodes can make progress.
	for i := 0; i < 2; i++ {
		events := cluster.events[i]
		log.Infof("waiting for event to be commited on node %v", i)
		commit := <-events.CommandCommitted
		if string(commit.Command) != "command" {
			t.Errorf("unexpected value in committed command: %v", commit.Command)
		}
	}

	// Ensure that node 2 is in fact blocked.
	time.Sleep(time.Millisecond)
	select {
	case commit := <-cluster.events[2].CommandCommitted:
		t.Errorf("didn't expect commits on node 2 but got %v", commit)
	default:
	}

	// After unblocking the third node, it will catch up.
	cluster.storages[2].Unblock()
	cluster.tickers[0].Tick()
	log.Infof("waiting for event to be commited on node 2")
	commit := <-cluster.events[2].CommandCommitted
	if string(commit.Command) != "command" {
		t.Errorf("unexpected value in committed command: %v", commit.Command)
	}
}

func TestMembershipChange(t *testing.T) {
	defer leaktest.AfterTest(t)
	cluster := newTestCluster(nil, 4, t)
	defer cluster.stop()

	// Create a group with a single member, cluster.nodes[0].
	groupID := uint64(1)
	cluster.createGroup(groupID, 0, 1)
	cluster.triggerElection(0, groupID)
	cluster.waitForElection(0)

	// Consume and apply the membership change events.
	for i := 0; i < 4; i++ {
		go func(i int) {
			for {
				e, ok := <-cluster.events[i].MembershipChangeCommitted
				if !ok {
					return
				}
				e.Callback(nil)
			}
		}(i)
	}

	// Add each of the other three nodes to the cluster.
	for i := 1; i < 4; i++ {
		ch := cluster.nodes[0].ChangeGroupMembership(groupID, makeCommandID(),
			raftpb.ConfChangeAddNode,
			cluster.nodes[i].nodeID, nil)
		<-ch
	}

	// TODO(bdarnell): verify that the channel events are sent out correctly.
	/*
		for i := 0; i < 10; i++ {
			log.Infof("tick %d", i)
			cluster.tickers[0].Tick()
			time.Sleep(5 * time.Millisecond)
		}

		// Each node is notified of each other node's joining.
		for i := 0; i < 4; i++ {
			for j := 1; j < 4; j++ {
				select {
				case e := <-cluster.events[i].MembershipChangeCommitted:
					if e.NodeID != cluster.nodes[j].nodeID {
						t.Errorf("node %d expected event for %d, got %d", i, j, e.NodeID)
					}
				default:
					t.Errorf("node %d did not get expected event for %d", i, j)
				}
			}
		}*/
}
<|endoftext|>


<commit_before>// Copyright 2018 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.

// The vet command is a static checker for Go programs. It has pluggable
// analyzers defined using the golang.org/x/tools/go/analysis API, and
// using the golang.org/x/tools/go/packages API to load packages in any
// build system.
//
// Each analyzer flag name is preceded by the analyzer name: -NAME.flag.
// In addition, the -NAME flag itself controls whether the
// diagnostics of that analyzer are displayed. (A disabled analyzer may yet
// be run if it is required by some other analyzer that is enabled.)
package main

import (
	"golang.org/x/tools/go/analysis/multichecker"

	// analysis plug-ins
	"golang.org/x/tools/go/analysis/passes/asmdecl"
	"golang.org/x/tools/go/analysis/passes/assign"
	"golang.org/x/tools/go/analysis/passes/atomic"
	"golang.org/x/tools/go/analysis/passes/atomicalign"
	"golang.org/x/tools/go/analysis/passes/bools"
	"golang.org/x/tools/go/analysis/passes/buildtag"
	"golang.org/x/tools/go/analysis/passes/cgocall"
	"golang.org/x/tools/go/analysis/passes/composite"
	"golang.org/x/tools/go/analysis/passes/copylock"
	"golang.org/x/tools/go/analysis/passes/httpresponse"
	"golang.org/x/tools/go/analysis/passes/loopclosure"
	"golang.org/x/tools/go/analysis/passes/lostcancel"
	"golang.org/x/tools/go/analysis/passes/nilfunc"
	"golang.org/x/tools/go/analysis/passes/nilness"
	"golang.org/x/tools/go/analysis/passes/printf"
	"golang.org/x/tools/go/analysis/passes/shift"
	"golang.org/x/tools/go/analysis/passes/stdmethods"
	"golang.org/x/tools/go/analysis/passes/structtag"
	"golang.org/x/tools/go/analysis/passes/tests"
	"golang.org/x/tools/go/analysis/passes/unmarshal"
	"golang.org/x/tools/go/analysis/passes/unreachable"
	"golang.org/x/tools/go/analysis/passes/unsafeptr"
	"golang.org/x/tools/go/analysis/passes/unusedresult"
)

func main() {
	// This suite of analyzers is applied to all code
	// in GOROOT by GOROOT/src/cmd/vet/all. When adding
	// a new analyzer, update the whitelist used by vet/all,
	// or change its vet command to disable the new analyzer.
	multichecker.Main(
		// the traditional vet suite:
		asmdecl.Analyzer,
		assign.Analyzer,
		atomic.Analyzer,
		atomicalign.Analyzer,
		bools.Analyzer,
		buildtag.Analyzer,
		cgocall.Analyzer,
		composite.Analyzer,
		copylock.Analyzer,
		httpresponse.Analyzer,
		loopclosure.Analyzer,
		lostcancel.Analyzer,
		nilfunc.Analyzer,
		printf.Analyzer,
		shift.Analyzer,
		stdmethods.Analyzer,
		structtag.Analyzer,
		tests.Analyzer,
		unmarshal.Analyzer,
		unreachable.Analyzer,
		unsafeptr.Analyzer,
		unusedresult.Analyzer,

		// for debugging:
		// findcall.Analyzer,
		// pkgfact.Analyzer,

		// uses SSA:
		nilness.Analyzer,
	)
}
<commit_msg>go/analysis/cmd/vet: add errorsas analyzer<commit_after>// Copyright 2018 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.

// The vet command is a static checker for Go programs. It has pluggable
// analyzers defined using the golang.org/x/tools/go/analysis API, and
// using the golang.org/x/tools/go/packages API to load packages in any
// build system.
//
// Each analyzer flag name is preceded by the analyzer name: -NAME.flag.
// In addition, the -NAME flag itself controls whether the
// diagnostics of that analyzer are displayed. (A disabled analyzer may yet
// be run if it is required by some other analyzer that is enabled.)
package main

import (
	"golang.org/x/tools/go/analysis/multichecker"

	// analysis plug-ins
	"golang.org/x/tools/go/analysis/passes/asmdecl"
	"golang.org/x/tools/go/analysis/passes/assign"
	"golang.org/x/tools/go/analysis/passes/atomic"
	"golang.org/x/tools/go/analysis/passes/atomicalign"
	"golang.org/x/tools/go/analysis/passes/bools"
	"golang.org/x/tools/go/analysis/passes/buildtag"
	"golang.org/x/tools/go/analysis/passes/cgocall"
	"golang.org/x/tools/go/analysis/passes/composite"
	"golang.org/x/tools/go/analysis/passes/copylock"
	"golang.org/x/tools/go/analysis/passes/errorsas"
	"golang.org/x/tools/go/analysis/passes/httpresponse"
	"golang.org/x/tools/go/analysis/passes/loopclosure"
	"golang.org/x/tools/go/analysis/passes/lostcancel"
	"golang.org/x/tools/go/analysis/passes/nilfunc"
	"golang.org/x/tools/go/analysis/passes/nilness"
	"golang.org/x/tools/go/analysis/passes/printf"
	"golang.org/x/tools/go/analysis/passes/shift"
	"golang.org/x/tools/go/analysis/passes/stdmethods"
	"golang.org/x/tools/go/analysis/passes/structtag"
	"golang.org/x/tools/go/analysis/passes/tests"
	"golang.org/x/tools/go/analysis/passes/unmarshal"
	"golang.org/x/tools/go/analysis/passes/unreachable"
	"golang.org/x/tools/go/analysis/passes/unsafeptr"
	"golang.org/x/tools/go/analysis/passes/unusedresult"
)

func main() {
	// This suite of analyzers is applied to all code
	// in GOROOT by GOROOT/src/cmd/vet/all. When adding
	// a new analyzer, update the whitelist used by vet/all,
	// or change its vet command to disable the new analyzer.
	multichecker.Main(
		// the traditional vet suite:
		asmdecl.Analyzer,
		assign.Analyzer,
		atomic.Analyzer,
		atomicalign.Analyzer,
		bools.Analyzer,
		buildtag.Analyzer,
		cgocall.Analyzer,
		composite.Analyzer,
		copylock.Analyzer,
		errorsas.Analyzer,
		httpresponse.Analyzer,
		loopclosure.Analyzer,
		lostcancel.Analyzer,
		nilfunc.Analyzer,
		printf.Analyzer,
		shift.Analyzer,
		stdmethods.Analyzer,
		structtag.Analyzer,
		tests.Analyzer,
		unmarshal.Analyzer,
		unreachable.Analyzer,
		unsafeptr.Analyzer,
		unusedresult.Analyzer,

		// for debugging:
		// findcall.Analyzer,
		// pkgfact.Analyzer,

		// uses SSA:
		nilness.Analyzer,
	)
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Vitess Authors.

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 mysql

import (
	"errors"
	"fmt"
	"io"
	"strconv"
	"strings"
	"time"

	"golang.org/x/net/context"
)

type filePosFlavor struct {
	format     BinlogFormat
	file       string
	savedEvent BinlogEvent
}

// newFilePosFlavor creates a new filePos flavor.
func newFilePosFlavor() flavor {
	return &filePosFlavor{}
}

// masterGTIDSet is part of the Flavor interface.
func (flv *filePosFlavor) masterGTIDSet(c *Conn) (GTIDSet, error) {
	qr, err := c.ExecuteFetch("SHOW SLAVE STATUS", 100, true /* wantfields */)
	if err != nil {
		return nil, err
	}
	if len(qr.Rows) == 0 {
		qr, err = c.ExecuteFetch("SHOW MASTER STATUS", 100, true /* wantfields */)
		if err != nil {
			return nil, err
		}
		if len(qr.Rows) == 0 {
			return nil, errors.New("no master or slave status")
		}
		resultMap, err := resultToMap(qr)
		if err != nil {
			return nil, err
		}
		pos, err := strconv.Atoi(resultMap["Position"])
		if err != nil {
			return nil, fmt.Errorf("invalid FilePos GTID (%v): expecting pos to be an integer", resultMap["Position"])
		}

		return filePosGTID{
			file: resultMap["File"],
			pos:  pos,
		}, nil
	}

	resultMap, err := resultToMap(qr)
	if err != nil {
		return nil, err
	}
	pos, err := strconv.Atoi(resultMap["Exec_Master_Log_Pos"])
	if err != nil {
		return nil, fmt.Errorf("invalid FilePos GTID (%v): expecting pos to be an integer", resultMap["Exec_Master_Log_Pos"])
	}

	return filePosGTID{
		file: resultMap["Relay_Master_Log_File"],
		pos:  pos,
	}, nil
}

func (flv *filePosFlavor) startSlaveCommand() string {
	return "unsupported"
}

func (flv *filePosFlavor) restartSlaveCommands() []string {
	return []string{"unsupported"}
}

func (flv *filePosFlavor) stopSlaveCommand() string {
	return "unsupported"
}

// sendBinlogDumpCommand is part of the Flavor interface.
func (flv *filePosFlavor) sendBinlogDumpCommand(c *Conn, slaveID uint32, startPos Position) error {
	rpos, ok := startPos.GTIDSet.(filePosGTID)
	if !ok {
		return fmt.Errorf("startPos.GTIDSet is wrong type - expected filePosGTID, got: %#v", startPos.GTIDSet)
	}

	flv.file = rpos.file
	return c.WriteComBinlogDump(slaveID, rpos.file, uint32(rpos.pos), 0)
}

// readBinlogEvent is part of the Flavor interface.
func (flv *filePosFlavor) readBinlogEvent(c *Conn) (BinlogEvent, error) {
	if ret := flv.savedEvent; ret != nil {
		flv.savedEvent = nil
		return ret, nil
	}

	for {
		result, err := c.ReadPacket()
		if err != nil {
			return nil, err
		}
		switch result[0] {
		case EOFPacket:
			return nil, NewSQLError(CRServerLost, SSUnknownSQLState, "%v", io.EOF)
		case ErrPacket:
			return nil, ParseErrorPacket(result)
		}

		event := &filePosBinlogEvent{binlogEvent: binlogEvent(result[1:])}
		switch event.Type() {
		case eGTIDEvent, eAnonymousGTIDEvent, ePreviousGTIDsEvent, eMariaGTIDListEvent:
			// Don't transmit fake or irrelevant events because we should not
			// resume replication at these positions.
			continue
		case eMariaGTIDEvent:
			// Copied from mariadb flavor.
			const FLStandalone = 1
			flags2 := result[8+4]
			// This means that it's also a BEGIN event.
			if flags2&FLStandalone == 0 {
				return newFilePosQueryEvent("begin", event.Timestamp()), nil
			}
			// Otherwise, don't send this event.
			continue
		case eFormatDescriptionEvent:
			format, err := event.Format()
			if err != nil {
				return nil, err
			}
			flv.format = format
		case eRotateEvent:
			if !flv.format.IsZero() {
				stripped, _, _ := event.StripChecksum(flv.format)
				_, flv.file = stripped.(*filePosBinlogEvent).rotate(flv.format)
				// No need to transmit. Just update the internal position for the next event.
				continue
			}
		case eXIDEvent, eTableMapEvent,
			eWriteRowsEventV0, eWriteRowsEventV1, eWriteRowsEventV2,
			eDeleteRowsEventV0, eDeleteRowsEventV1, eDeleteRowsEventV2,
			eUpdateRowsEventV0, eUpdateRowsEventV1, eUpdateRowsEventV2:
			flv.savedEvent = event
			return newFilePosGTIDEvent(flv.file, event.nextPosition(flv.format), event.Timestamp()), nil
		case eQueryEvent:
			q, err := event.Query(flv.format)
			if err == nil && strings.HasPrefix(q.SQL, "#") {
				continue
			}
			flv.savedEvent = event
			return newFilePosGTIDEvent(flv.file, event.nextPosition(flv.format), event.Timestamp()), nil
		default:
			// For unrecognized events, send a fake "repair" event so that
			// the position gets transmitted.
			if !flv.format.IsZero() {
				if v := event.nextPosition(flv.format); v != 0 {
					flv.savedEvent = newFilePosQueryEvent("repair", event.Timestamp())
					return newFilePosGTIDEvent(flv.file, v, event.Timestamp()), nil
				}
			}
		}
		return event, nil
	}
}

// resetReplicationCommands is part of the Flavor interface.
func (flv *filePosFlavor) resetReplicationCommands(c *Conn) []string {
	return []string{
		"unsupported",
	}
}

// setSlavePositionCommands is part of the Flavor interface.
func (flv *filePosFlavor) setSlavePositionCommands(pos Position) []string {
	return []string{
		"unsupported",
	}
}

// setSlavePositionCommands is part of the Flavor interface.
func (flv *filePosFlavor) changeMasterArg() string {
	return "unsupported"
}

// status is part of the Flavor interface.
func (flv *filePosFlavor) status(c *Conn) (SlaveStatus, error) {
	qr, err := c.ExecuteFetch("SHOW SLAVE STATUS", 100, true /* wantfields */)
	if err != nil {
		return SlaveStatus{}, err
	}
	if len(qr.Rows) == 0 {
		// The query returned no data, meaning the server
		// is not configured as a slave.
		return SlaveStatus{}, ErrNotSlave
	}

	resultMap, err := resultToMap(qr)
	if err != nil {
		return SlaveStatus{}, err
	}

	status := parseSlaveStatus(resultMap)
	pos, err := strconv.Atoi(resultMap["Exec_Master_Log_Pos"])
	if err != nil {
		return SlaveStatus{}, fmt.Errorf("invalid FilePos GTID (%v): expecting pos to be an integer", resultMap["Exec_Master_Log_Pos"])
	}

	status.Position.GTIDSet = filePosGTID{
		file: resultMap["Relay_Master_Log_File"],
		pos:  pos,
	}
	return status, nil
}

// waitUntilPositionCommand is part of the Flavor interface.
func (flv *filePosFlavor) waitUntilPositionCommand(ctx context.Context, pos Position) (string, error) {
	filePosPos, ok := pos.GTIDSet.(filePosGTID)
	if !ok {
		return "", fmt.Errorf("Position is not filePos compatible: %#v", pos.GTIDSet)
	}

	if deadline, ok := ctx.Deadline(); ok {
		timeout := time.Until(deadline)
		if timeout <= 0 {
			return "", fmt.Errorf("timed out waiting for position %v", pos)
		}
		return fmt.Sprintf("SELECT MASTER_POS_WAIT('%s', %d, %.6f)", filePosPos.file, filePosPos.pos, timeout.Seconds()), nil
	}

	return fmt.Sprintf("SELECT MASTER_POS_WAIT('%s', %d)", filePosPos.file, filePosPos.pos), nil
}

func (*filePosFlavor) startSlaveUntilAfter(pos Position) string {
	return "unsupported"
}

// enableBinlogPlaybackCommand is part of the Flavor interface.
func (*filePosFlavor) enableBinlogPlaybackCommand() string {
	return ""
}

// disableBinlogPlaybackCommand is part of the Flavor interface.
func (*filePosFlavor) disableBinlogPlaybackCommand() string {
	return ""
}
<commit_msg>Updates how master gtid position is obtained for file:pos flavor<commit_after>/*
Copyright 2019 The Vitess Authors.

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 mysql

import (
	"errors"
	"fmt"
	"io"
	"strconv"
	"strings"
	"time"

	"golang.org/x/net/context"
)

type filePosFlavor struct {
	format     BinlogFormat
	file       string
	savedEvent BinlogEvent
}

// newFilePosFlavor creates a new filePos flavor.
func newFilePosFlavor() flavor {
	return &filePosFlavor{}
}

// masterGTIDSet is part of the Flavor interface.
func (flv *filePosFlavor) masterGTIDSet(c *Conn) (GTIDSet, error) {
	qr, err := c.ExecuteFetch("SHOW MASTER STATUS", 100, true /* wantfields */)
	if err != nil {
		return nil, err
	}
	if len(qr.Rows) == 0 {
		return nil, errors.New("no master status")
	}

	resultMap, err := resultToMap(qr)
	if err != nil {
		return nil, err
	}
	pos, err := strconv.Atoi(resultMap["Position"])
	if err != nil {
		return nil, fmt.Errorf("invalid FilePos GTID (%v): expecting pos to be an integer", resultMap["Position"])
	}

	return filePosGTID{
		file: resultMap["File"],
		pos:  pos,
	}, nil
}

func (flv *filePosFlavor) startSlaveCommand() string {
	return "unsupported"
}

func (flv *filePosFlavor) restartSlaveCommands() []string {
	return []string{"unsupported"}
}

func (flv *filePosFlavor) stopSlaveCommand() string {
	return "unsupported"
}

// sendBinlogDumpCommand is part of the Flavor interface.
func (flv *filePosFlavor) sendBinlogDumpCommand(c *Conn, slaveID uint32, startPos Position) error {
	rpos, ok := startPos.GTIDSet.(filePosGTID)
	if !ok {
		return fmt.Errorf("startPos.GTIDSet is wrong type - expected filePosGTID, got: %#v", startPos.GTIDSet)
	}

	flv.file = rpos.file
	return c.WriteComBinlogDump(slaveID, rpos.file, uint32(rpos.pos), 0)
}

// readBinlogEvent is part of the Flavor interface.
func (flv *filePosFlavor) readBinlogEvent(c *Conn) (BinlogEvent, error) {
	if ret := flv.savedEvent; ret != nil {
		flv.savedEvent = nil
		return ret, nil
	}

	for {
		result, err := c.ReadPacket()
		if err != nil {
			return nil, err
		}
		switch result[0] {
		case EOFPacket:
			return nil, NewSQLError(CRServerLost, SSUnknownSQLState, "%v", io.EOF)
		case ErrPacket:
			return nil, ParseErrorPacket(result)
		}

		event := &filePosBinlogEvent{binlogEvent: binlogEvent(result[1:])}
		switch event.Type() {
		case eGTIDEvent, eAnonymousGTIDEvent, ePreviousGTIDsEvent, eMariaGTIDListEvent:
			// Don't transmit fake or irrelevant events because we should not
			// resume replication at these positions.
			continue
		case eMariaGTIDEvent:
			// Copied from mariadb flavor.
			const FLStandalone = 1
			flags2 := result[8+4]
			// This means that it's also a BEGIN event.
			if flags2&FLStandalone == 0 {
				return newFilePosQueryEvent("begin", event.Timestamp()), nil
			}
			// Otherwise, don't send this event.
			continue
		case eFormatDescriptionEvent:
			format, err := event.Format()
			if err != nil {
				return nil, err
			}
			flv.format = format
		case eRotateEvent:
			if !flv.format.IsZero() {
				stripped, _, _ := event.StripChecksum(flv.format)
				_, flv.file = stripped.(*filePosBinlogEvent).rotate(flv.format)
				// No need to transmit. Just update the internal position for the next event.
				continue
			}
		case eXIDEvent, eTableMapEvent,
			eWriteRowsEventV0, eWriteRowsEventV1, eWriteRowsEventV2,
			eDeleteRowsEventV0, eDeleteRowsEventV1, eDeleteRowsEventV2,
			eUpdateRowsEventV0, eUpdateRowsEventV1, eUpdateRowsEventV2:
			flv.savedEvent = event
			return newFilePosGTIDEvent(flv.file, event.nextPosition(flv.format), event.Timestamp()), nil
		case eQueryEvent:
			q, err := event.Query(flv.format)
			if err == nil && strings.HasPrefix(q.SQL, "#") {
				continue
			}
			flv.savedEvent = event
			return newFilePosGTIDEvent(flv.file, event.nextPosition(flv.format), event.Timestamp()), nil
		default:
			// For unrecognized events, send a fake "repair" event so that
			// the position gets transmitted.
			if !flv.format.IsZero() {
				if v := event.nextPosition(flv.format); v != 0 {
					flv.savedEvent = newFilePosQueryEvent("repair", event.Timestamp())
					return newFilePosGTIDEvent(flv.file, v, event.Timestamp()), nil
				}
			}
		}
		return event, nil
	}
}

// resetReplicationCommands is part of the Flavor interface.
func (flv *filePosFlavor) resetReplicationCommands(c *Conn) []string {
	return []string{
		"unsupported",
	}
}

// setSlavePositionCommands is part of the Flavor interface.
func (flv *filePosFlavor) setSlavePositionCommands(pos Position) []string {
	return []string{
		"unsupported",
	}
}

// setSlavePositionCommands is part of the Flavor interface.
func (flv *filePosFlavor) changeMasterArg() string {
	return "unsupported"
}

// status is part of the Flavor interface.
func (flv *filePosFlavor) status(c *Conn) (SlaveStatus, error) {
	qr, err := c.ExecuteFetch("SHOW SLAVE STATUS", 100, true /* wantfields */)
	if err != nil {
		return SlaveStatus{}, err
	}
	if len(qr.Rows) == 0 {
		// The query returned no data, meaning the server
		// is not configured as a slave.
		return SlaveStatus{}, ErrNotSlave
	}

	resultMap, err := resultToMap(qr)
	if err != nil {
		return SlaveStatus{}, err
	}

	status := parseSlaveStatus(resultMap)
	pos, err := strconv.Atoi(resultMap["Exec_Master_Log_Pos"])
	if err != nil {
		return SlaveStatus{}, fmt.Errorf("invalid FilePos GTID (%v): expecting pos to be an integer", resultMap["Exec_Master_Log_Pos"])
	}

	status.Position.GTIDSet = filePosGTID{
		file: resultMap["Relay_Master_Log_File"],
		pos:  pos,
	}
	return status, nil
}

// waitUntilPositionCommand is part of the Flavor interface.
func (flv *filePosFlavor) waitUntilPositionCommand(ctx context.Context, pos Position) (string, error) {
	filePosPos, ok := pos.GTIDSet.(filePosGTID)
	if !ok {
		return "", fmt.Errorf("Position is not filePos compatible: %#v", pos.GTIDSet)
	}

	if deadline, ok := ctx.Deadline(); ok {
		timeout := time.Until(deadline)
		if timeout <= 0 {
			return "", fmt.Errorf("timed out waiting for position %v", pos)
		}
		return fmt.Sprintf("SELECT MASTER_POS_WAIT('%s', %d, %.6f)", filePosPos.file, filePosPos.pos, timeout.Seconds()), nil
	}

	return fmt.Sprintf("SELECT MASTER_POS_WAIT('%s', %d)", filePosPos.file, filePosPos.pos), nil
}

func (*filePosFlavor) startSlaveUntilAfter(pos Position) string {
	return "unsupported"
}

// enableBinlogPlaybackCommand is part of the Flavor interface.
func (*filePosFlavor) enableBinlogPlaybackCommand() string {
	return ""
}

// disableBinlogPlaybackCommand is part of the Flavor interface.
func (*filePosFlavor) disableBinlogPlaybackCommand() string {
	return ""
}
<|endoftext|>


<commit_before>package tenho

import (
	"fmt"
	"math/rand"
	"sort"
	"time"
)

func Start() {
	start := time.Now()
	// 処理
	var i float64
	i = 0
	for {
		i++
		end := time.Now()
		diff := float64(end.Sub(start) / 1000 / 1000 / 1000)
		m := int(i / diff)
		out := 0
		if m >= 0 {
			out = m
		}
		fmt.Printf("\r")
		fmt.Printf("\r%v回試行  %v秒経過 %v回/秒", i, diff, out)
		seed := time.Now().UnixNano()
		var hai string
		var ok bool
		hai, ok = tryOnce(seed)
		fmt.Printf("%v", hai)
		if ok {
			break
		}
	}
	fmt.Printf("\n")
}

type Hand []int

func tryOnce(seed int64) (string, bool) {
	hand := ShuffledHand(seed)
	hai := hand.HaiString()
	ok := Solve(hand)
	return hai, ok
}

// http://d.hatena.ne.jp/hake/20150930/p1
func shuffle(hand Hand) {
	for i := len(hand); i > 1; i-- {
		j := rand.Intn(i) // 0 .. i-1 の乱数発生
		hand[i-1], hand[j] = hand[j], hand[i-1]
	}
}

func ShuffledHand(seed int64) Hand {
	rand.Seed(seed)

	// データ要素数指定、および初期データ作成
	size := 136
	hand := make(Hand, size, size)
	for i := 0; i < size; i++ {
		hand[i] = i / 4
	}

	// シャッフル
	shuffle(hand)

	return hand[:14]
}

// 牌文字への変換(スペース区切り)
func (c Hand) HaiString() string {
	// http://qiita.com/ruiu/items/2bb83b29baeae2433a79
	// サイズ0、内部バッファの長さ69の[]byteの値を割り当てる
	b := make([]byte, 0, 70)

	// bに文字列を追加
	for j := 0; j < 14; j++ {
		// コードポイント上、普通の麻雀牌はU+1F000からの34個。
		// U+1F000 is 'MAHJONG TILE EAST WIND' ('東')
		// https://codepoints.net/U+1F000
		b = append(b, string(c[j]+0x1F000)...) // appendするには...が必要
		// 自分のMacではスペース区切りでないとうまく表示されないためスペースを挿入する
		// U+0020 is 'SPACE'
		// https://codepoints.net/U+0020
		b = append(b, string(0x20)...) // appendするには...が必要
	}
	return string(b)
}

// あがり判定する
func Solve(hand Hand) bool {
	if is_chitoitsu(hand) {
		return true
	}
	suit_grouped_hand := hand.GroupSuit()
	//fmt.Printf("%v", suit_grouped_hand)

	return suit_grouped_hand.Solve()
}

type SuitGroup []int

type SuitsGroupedHand []SuitGroup

// スート分類
func (hand Hand) GroupSuit() SuitsGroupedHand {
	m := SuitsGroupedHand{{}, {}, {}, {}}
	for _, i := range hand {
		switch {
		case i < 7:
			m[0] = append(m[0], i)
		case i < 7+(9*1):
			m[1] = append(m[1], i-7)
		case i < 7+(9*2):
			m[2] = append(m[2], i-7-(9*1))
		case i < 7+(9*3):
			m[3] = append(m[3], i-7-(9*2))
		}
	}
	return m
}

func (m SuitsGroupedHand) Solve() bool {
	return m.a_pair_existible() && m.valid_33332()
}

func (m SuitsGroupedHand) a_pair_existible() bool {
	//スートのサイズを3で割った時
	//あまりが2であるスートグループが1つであること
	c := 0
	for _, a := range m {
		switch len(a) % 3 {
		case 0:
			// noop
		case 1:
			return false
		case 2:
			c++
		}
	}
	return c == 1
}

func (m SuitsGroupedHand) valid_33332() bool {
	for i := 0; i < 4; i++ {
		if !valid_suit_group(m[i], i) {
			return false
		}
	}
	return true
}

func valid_suit_group(a SuitGroup, i int) bool {
	// 第二引数は字牌のとき0

	//ソート
	sort.Ints(a)
	if len(a)%3 == 2 {
		//ペアを探す
		pair_numbers := pairable_numbers(a)
		//ペア候補がなかったらぬける
		if len(pair_numbers) == 0 {
			return false
		}
		//ペア候補毎に繰り返し処理
		for _, v := range pair_numbers {
			//ペアとなる２枚を除去
			rest := SuitGroup{}
			c := 2
			for _, w := range a {
				// ペア候補以外は新スライスに入れる
				// ペア候補は３枚目以降は新スライスに入れる
				if w != v || c <= 0 {
					rest = append(rest, w)
				}
				if w == v {
					c--
				}
			}
			if valid_3cards(rest, i) {
				return true
			} else {
				continue
			}
		}
		return false
	} else if len(a)%3 == 0 {
		return valid_3cards(a, i)
	}
	// 到達しないはず
	panic("到達しないはず")
}

func valid_3cards(a SuitGroup, i int) bool {
	// 刻子や順子のみで構成されている場合true
	// a is sorted
	// a.size % 3 is0
	// 第二引数は字牌のとき0
	ok := false
	for {
		a, ok = remove_kotsu(a)
		if ok {
			continue
		}
		if i > 0 {
			a, ok = remove_shuntsu(a)
			if ok {
				continue
			}
		}
		return len(a) == 0
	}
}

func remove_kotsu(a SuitGroup) (SuitGroup, bool) {
	// 刻子を除去できればtrue
	// a is sorted
	retval := a
	if len(a) < 3 {
		return retval, false
	}
	if a[0] == a[1] && a[0] == a[2] {
		retval = a[3:]
		return retval, true
	}
	return retval, false
}

func remove_shuntsu(a SuitGroup) (SuitGroup, bool) {
	// 順子を除去できればtrue
	// a is sorted
	rest := SuitGroup{}
	first := -1
	second := -1
	found := false
	for _, v := range a {
		if found {
			rest = append(rest, v)
			continue
		}
		if first == -1 {
			first = v
		} else if second == -1 && first+1 == v {
			second = v
		} else if second != -1 && first+2 == v {
			//flush
			first = -1
			second = -1
			found = true
		} else {
			rest = append(rest, v)
		}
	}
	return rest, found
}

func pairable_numbers(sorted SuitGroup) SuitGroup {
	counter := []int{}
	a := 999 // 2つ前
	b := 999 // 1つ前
	for _, v := range sorted {
		if b == v && a != v {
			counter = append(counter, v)
		} else {
			b = v
		}
	}
	return counter
}

func is_chitoitsu(hand Hand) bool {
	//カウンタ
	c := map[int]int{}

	for _, v := range hand {
		count, ok := c[v]
		if ok {
			if count == 1 {
				c[v] = 2
			} else {
				// c[v] == 2
				return false
			}
		} else {
			c[v] = 1
		}
		//8個チェック
		if len(c) >= 8 {
			return false
		}
	}
	return true
}
<commit_msg>Refactor<commit_after>package tenho

import (
	"fmt"
	"math/rand"
	"sort"
	"time"
)

func Start() {
	start := time.Now()
	// 処理
	var i float64
	i = 0
	for {
		i++
		end := time.Now()
		diff := float64(end.Sub(start) / 1000 / 1000 / 1000)
		m := int(i / diff)
		out := 0
		if m >= 0 {
			out = m
		}
		fmt.Printf("\r")
		fmt.Printf("\r%v回試行  %v秒経過 %v回/秒", i, diff, out)
		seed := time.Now().UnixNano()
		var hai string
		var ok bool
		hai, ok = tryOnce(seed)
		fmt.Printf("%v", hai)
		if ok {
			break
		}
	}
	fmt.Printf("\n")
}

type Hand []int

func tryOnce(seed int64) (string, bool) {
	hand := ShuffledHand(seed)
	hai := hand.HaiString()
	ok := Solve(hand)
	return hai, ok
}

// http://d.hatena.ne.jp/hake/20150930/p1
func shuffle(hand Hand) {
	for i := len(hand); i > 1; i-- {
		j := rand.Intn(i) // 0 .. i-1 の乱数発生
		hand[i-1], hand[j] = hand[j], hand[i-1]
	}
}

func ShuffledHand(seed int64) Hand {
	rand.Seed(seed)

	// データ要素数指定、および初期データ作成
	size := 136
	hand := make(Hand, size, size)
	for i := 0; i < size; i++ {
		hand[i] = i / 4
	}

	// シャッフル
	shuffle(hand)

	return hand[:14]
}

// 牌文字への変換(スペース区切り)
func (c Hand) HaiString() string {
	// http://qiita.com/ruiu/items/2bb83b29baeae2433a79
	// サイズ0、内部バッファの長さ69の[]byteの値を割り当てる
	b := make([]byte, 0, 70)

	// bに文字列を追加
	for j := 0; j < 14; j++ {
		// コードポイント上、普通の麻雀牌はU+1F000からの34個。
		// U+1F000 is 'MAHJONG TILE EAST WIND' ('東')
		// https://codepoints.net/U+1F000
		b = append(b, string(c[j]+0x1F000)...) // appendするには...が必要
		// 自分のMacではスペース区切りでないとうまく表示されないためスペースを挿入する
		// U+0020 is 'SPACE'
		// https://codepoints.net/U+0020
		b = append(b, string(0x20)...) // appendするには...が必要
	}
	return string(b)
}

// あがり判定する
func Solve(hand Hand) bool {
	if is_chitoitsu(hand) {
		return true
	}
	suit_grouped_hand := hand.GroupSuit()
	//fmt.Printf("%v", suit_grouped_hand)

	return suit_grouped_hand.Solve()
}

type SuitGroup []int

type SuitsGroupedHand []SuitGroup

// スート分類
func (hand Hand) GroupSuit() SuitsGroupedHand {
	m := SuitsGroupedHand{{}, {}, {}, {}}
	for _, i := range hand {
		switch {
		case i < 7:
			m[0] = append(m[0], i)
		case i < 7+(9*1):
			m[1] = append(m[1], i-7)
		case i < 7+(9*2):
			m[2] = append(m[2], i-7-(9*1))
		case i < 7+(9*3):
			m[3] = append(m[3], i-7-(9*2))
		}
	}
	return m
}

func (m SuitsGroupedHand) Solve() bool {
	return m.a_pair_existible() && m.valid_33332()
}

func (m SuitsGroupedHand) a_pair_existible() bool {
	//スートのサイズを3で割った時
	//あまりが2であるスートグループが1つであること
	c := 0
	for _, a := range m {
		switch len(a) % 3 {
		case 0:
			// noop
		case 1:
			return false
		case 2:
			c++
		}
	}
	return c == 1
}

func (m SuitsGroupedHand) valid_33332() bool {
	for i := 0; i < 4; i++ {
		if !m[i].valid_suit_group(i) {
			return false
		}
	}
	return true
}

// 33332形を形成するスートグループがどうかを判定
func (a SuitGroup) valid_suit_group(i int) bool {
	// 対子が含まれているスートグループがただ1つある前提

	//ソート
	sort.Ints(a)
	if len(a)%3 == 2 {
		//ペアを探す
		pair_numbers := pairable_numbers(a)
		//ペア候補がなかったらぬける
		if len(pair_numbers) == 0 {
			return false
		}
		//ペア候補毎に繰り返し処理
		for _, v := range pair_numbers {
			//ペアとなる２枚を除去
			rest := SuitGroup{}
			c := 2
			for _, w := range a {
				// ペア候補以外は新スライスに入れる
				// ペア候補は３枚目以降は新スライスに入れる
				if w != v || c <= 0 {
					rest = append(rest, w)
				}
				if w == v {
					c--
				}
			}
			if valid_3cards(rest, i) {
				return true
			} else {
				continue
			}
		}
		return false
	} else if len(a)%3 == 0 {
		return valid_3cards(a, i)
	}
	// 到達しないはず
	panic("到達しないはず")
}

func valid_3cards(a SuitGroup, i int) bool {
	// 刻子や順子のみで構成されている場合true
	// a is sorted
	// a.size % 3 is0
	// 第二引数は字牌のとき0
	ok := false
	for {
		a, ok = remove_kotsu(a)
		if ok {
			continue
		}
		if i > 0 {
			a, ok = remove_shuntsu(a)
			if ok {
				continue
			}
		}
		return len(a) == 0
	}
}

func remove_kotsu(a SuitGroup) (SuitGroup, bool) {
	// 刻子を除去できればtrue
	// a is sorted
	retval := a
	if len(a) < 3 {
		return retval, false
	}
	if a[0] == a[1] && a[0] == a[2] {
		retval = a[3:]
		return retval, true
	}
	return retval, false
}

func remove_shuntsu(a SuitGroup) (SuitGroup, bool) {
	// 順子を除去できればtrue
	// a is sorted
	rest := SuitGroup{}
	first := -1
	second := -1
	found := false
	for _, v := range a {
		if found {
			rest = append(rest, v)
			continue
		}
		if first == -1 {
			first = v
		} else if second == -1 && first+1 == v {
			second = v
		} else if second != -1 && first+2 == v {
			//flush
			first = -1
			second = -1
			found = true
		} else {
			rest = append(rest, v)
		}
	}
	return rest, found
}

func pairable_numbers(sorted SuitGroup) SuitGroup {
	counter := []int{}
	a := 999 // 2つ前
	b := 999 // 1つ前
	for _, v := range sorted {
		if b == v && a != v {
			counter = append(counter, v)
		} else {
			b = v
		}
	}
	return counter
}

func is_chitoitsu(hand Hand) bool {
	//カウンタ
	c := map[int]int{}

	for _, v := range hand {
		count, ok := c[v]
		if ok {
			if count == 1 {
				c[v] = 2
			} else {
				// c[v] == 2
				return false
			}
		} else {
			c[v] = 1
		}
		//8個チェック
		if len(c) >= 8 {
			return false
		}
	}
	return true
}
<|endoftext|>