<commit_before>package bot

import (
	"bitbucket.org/mrd0ll4r/tbotapi"
	"fmt"
	"log"
	"math/rand"
	"os"
	"strings"
	"sync"
	"time"
)

const (
	msgChoices                     = "([r]ock, [p]aper or [s]cissors)"
	msgChoose                      = "Make your choice! " + msgChoices
	msgNoPrivateChat               = "I have lost track of our private chat. Please write me in a private chat and try again"
	msgAlreadyIngame               = "You are already in a game right now, finish that first."
	msgAlreadyIngameWillRemainOpen = msgAlreadyIngame + " Game will remain open..."
	msgGameOpened                  = "Game opened! Join with /join, abort with /abort"
	msgGameStarted                 = "Game started! Waiting for your choices in our private chats..."
	msgGameAborted                 = "Game aborted"
	msgOnlyCreatorCanAbort         = "Only the creator can abort a game"
	msgCreatorAlreadyInGame        = "The creator is already in the game, idiot"
)

type command string

const (
	cmdStart   = command("start")
	cmdStop    = command("stop")
	cmdNew     = command("new")
	cmdJoin    = command("join")
	cmdAbort   = command("abort")
	cmdUnknown = command("unknown")
)

func parseCommand(text string) command {
	text = strings.ToLower(text)
	if !strings.HasPrefix(text, "/") {
		return cmdUnknown
	}

	text = text[1:]
	switch text {
	case "new", "n", "new@" + api.Username:
		return cmdNew
	case "join", "j", "join@" + api.Username:
		return cmdJoin
	case "abort", "a", "abort@" + api.Username:
		return cmdAbort
	case "start", "start@" + api.Username:
		return cmdStart
	case "stop", "stop@" + api.Username:
		return cmdStop
	default:
		return cmdUnknown
	}
}

type choice string

const (
	choiceRock     = choice("rock")
	choicePaper    = choice("paper")
	choiceScissors = choice("scissors")
	choiceUnknown  = choice("unknown")
)

func parseChoice(text string) choice {
	text = strings.ToLower(text)
	switch text {
	case "rock", "r":
		return choiceRock
	case "paper", "p":
		return choicePaper
	case "scissors", "s":
		return choiceScissors
	default:
		return choiceUnknown
	}
}

var api *tbotapi.TelegramBotAPI

// RunBot runs a bot.
// It will block until either something very bad happens or closing is closed.
func RunBot(apiKey string, closing chan struct{}) {
	fmt.Println("Starting...")
	if fileExists("chats.json") {
		fmt.Println("Loading chats...")
		err := loadChatsFromFile("chats.json")
		if err != nil {
			fmt.Println("Could not load:", err.Error())
		}
	}

	fmt.Println("Starting bot...")
	api, err := tbotapi.New(apiKey)
	if err != nil {
		log.Fatal(err)
	}

	// just to show its working
	fmt.Printf("User ID: %d\n", api.ID)
	fmt.Printf("Bot Name: %s\n", api.Name)
	fmt.Printf("Bot Username: %s\n", api.Username)

	closed := make(chan struct{})
	wg := &sync.WaitGroup{}

	wg.Add(1)
	go func() {
		defer wg.Done()
		for {
			select {
			case <-closed:
				return
			case update := <-api.Updates:
				if update.Error() != nil {
					fmt.Printf("Update error: %s\n", update.Error())
					continue
				}

				upd := update.Update()
				switch upd.Type() {
				case tbotapi.MessageUpdate:
					handleMessage(*update.Update().Message, api)
				case tbotapi.InlineQueryUpdate, tbotapi.ChosenInlineResultUpdate:
				// ignore
				default:
					fmt.Printf("Ignoring unknown Update type.")
				}
			}
		}
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		for {
			select {
			case <-closed:
				return
			case <-time.After(10 * time.Minute):
				fmt.Println("Saving db...")
				err = dumpChatsToFile("chats.json")
				if err != nil {
					fmt.Println("Could not save:", err.Error())
				}
			}
		}
	}()

	// wait for the signal
	<-closing
	fmt.Println("Closing...")

	fmt.Println("Saving db...")
	err = dumpChatsToFile("chats.json")
	if err != nil {
		fmt.Println("Could not save:", err.Error())
	}

	fmt.Println("Closing bot...")
	// always close the API first, let it clean up the update loop
	api.Close() //this might take a while
	close(closed)
	wg.Wait()
}

func fileExists(filename string) bool {
	fi, err := os.Lstat(filename)
	if fi != nil || (err != nil && !os.IsNotExist(err)) {
		return true
	}
	return false
}

func handleMessage(msg tbotapi.Message, api *tbotapi.TelegramBotAPI) {
	typ := msg.Type()
	if typ != tbotapi.TextMessage {
		//ignore non-text messages for now
		return
	}
	text := *msg.Text
	if msg.Chat.IsPrivateChat() {
		fmt.Printf("<-%d, %s,\t%q\n", msg.ID, msg.Chat, text)
	} else {
		fmt.Printf("<-%d, %s(%s),\t%q\n", msg.ID, msg.Chat, msg.From, text)
	}

	if msg.Chat.IsPrivateChat() {
		//always update the list of private chats
		putChat(msg.From.ID, msg.Chat.ID)
	}

	if strings.HasPrefix(text, "/") {
		//command
		cmd := parseCommand(text)
		if cmd == cmdNew {
			game(msg, api)
			return
		}

		groupsLock.RLock()
		if c, ok := groups[msg.Chat.ID]; ok {
			c <- msg
		}
		groupsLock.RUnlock()
	} else {
		if msg.Chat.IsPrivateChat() {
			uid := msg.From.ID
			expectsLock.Lock()
			if expect, ok := expects[uid]; ok {
				switch parseChoice(text) {
				case choiceRock, choicePaper, choiceScissors:
					expect <- parseChoice(text)
					delete(expects, uid)
				default:
					reply(msg, api, "No understand")
				}
			}
			expectsLock.Unlock()
		}
	}
}

func game(msg tbotapi.Message, api *tbotapi.TelegramBotAPI) {
	if hasExpect(msg.From.ID) {
		reply(msg, api, msgAlreadyIngame)
		return
	}

	if msg.Chat.IsPrivateChat() {
		reply(msg, api, "You will play against the bot. "+msgChoose)

		eChan := make(chan choice)
		expectsLock.Lock()
		expects[msg.From.ID] = eChan
		expectsLock.Unlock()

		go func(original tbotapi.Message, api *tbotapi.TelegramBotAPI, expected chan choice) {
			choice := <-eChan

			botChoice := rand.Float64()

			var resp string
			if botChoice < (float64(1) / float64(3)) {
				resp = formatResponse("the bot", "you", choiceRock, choice)
			} else if botChoice < (float64(2) / float64(3)) {
				resp = formatResponse("the bot", "you", choicePaper, choice)
			} else {
				resp = formatResponse("the bot", "you", choiceScissors, choice)
			}

			reply(original, api, resp)
		}(msg, api, eChan)

	} else {
		//group mode

		if !hasChat(msg.From.ID) {
			reply(msg, api, msgNoPrivateChat)
			return
		}

		if hasGroup(msg.Chat.ID) {
			reply(msg, api, "This group already has an open game.")
			return
		}

		messages := make(chan tbotapi.Message)
		groups[msg.Chat.ID] = messages
		reply(msg, api, msgGameOpened)

		go func(original tbotapi.Message, api *tbotapi.TelegramBotAPI, messages chan tbotapi.Message) {
			var p1, p2 chan choice
			var partner tbotapi.User

		loop:
			for {
				msg := <-messages
				if msg.Type() != tbotapi.TextMessage {
					continue
				}
				text := *msg.Text
				switch parseCommand(text) {
				case cmdJoin:
					if msg.From.ID == original.From.ID {
						reply(original, api, msgCreatorAlreadyInGame)
					} else {
						expectsLock.Lock()
						if _, ok := expects[original.From.ID]; ok {
							reply(msg, api, msgAlreadyIngameWillRemainOpen)
							expectsLock.Unlock()
							continue loop
						}

						if _, ok := expects[msg.From.ID]; ok {
							reply(msg, api, msgAlreadyIngameWillRemainOpen)
							expectsLock.Unlock()
							continue loop
						}

						if !hasChat(msg.From.ID) {
							reply(msg, api, msgNoPrivateChat)
							expectsLock.Unlock()
							continue loop
						}

						groupsLock.Lock()
						delete(groups, original.Chat.ID)
						groupsLock.Unlock()
						p1 = make(chan choice)
						expects[original.From.ID] = p1
						p2 = make(chan choice)
						expects[msg.From.ID] = p2
						expectsLock.Unlock()

						p1Chat := chats[original.From.ID]
						p2Chat := chats[msg.From.ID]

						partner = msg.From

						sendTo(p1Chat, api, msgChoose)
						sendTo(p2Chat, api, msgChoose)
						reply(original, api, msgGameStarted)

						break loop
					}
				case cmdAbort:
					if msg.From.ID == original.From.ID {
						groupsLock.Lock()
						delete(groups, original.Chat.ID)
						groupsLock.Unlock()
						reply(original, api, msgGameAborted)
						return
					} else {
						reply(original, api, msgOnlyCreatorCanAbort)
					}
				}
			}

			// game running

			var choice1, choice2 choice

		nextloop:
			for {
				select {
				case choice1 = <-p1:
					if choice2 != "" {
						break nextloop
					}
				case choice2 = <-p2:
					if choice1 != "" {
						break nextloop
					}
				}
			}

			resp := formatResponse(original.From.String(), partner.String(), choice1, choice2)

			reply(original, api, resp)

		}(msg, api, messages)
	}
}

func formatResponse(part1, part2 string, choice1, choice2 choice) string {
	var res string
	switch choice1 {
	case choiceRock:
		switch choice2 {
		case choiceRock:
			res = "tie"
		case choicePaper:
			res = part2 + " wins"
		case choiceScissors:
			res = part1 + " wins"
		}
	case choicePaper:
		switch choice2 {
		case choiceRock:
			res = part1 + " wins"
		case choicePaper:
			res = "tie"
		case choiceScissors:
			res = part2 + " wins"
		}
	case choiceScissors:
		switch choice2 {
		case choiceRock:
			res = part2 + " wins"
		case choicePaper:
			res = part1 + " wins"
		case choiceScissors:
			res = "tie"
		}
	}

	return fmt.Sprintf("%s chose %s, %s chose %s: %s", part1, choice1, part2, choice2, res)
}

func sendTo(chat int, api *tbotapi.TelegramBotAPI, text string) error {
	outMsg, err := api.NewOutgoingMessage(tbotapi.NewChatRecipient(chat), text).Send()
	if err != nil {
		return err
	}

	fmt.Printf("->%d, %s,\t%q\n", outMsg.Message.ID, outMsg.Message.Chat, *outMsg.Message.Text)
	return nil
}

func reply(msg tbotapi.Message, api *tbotapi.TelegramBotAPI, text string) error {
	return sendTo(msg.Chat.ID, api, text)
}
<commit_msg>fixed parsing panic<commit_after>package bot

import (
	"bitbucket.org/mrd0ll4r/tbotapi"
	"fmt"
	"log"
	"math/rand"
	"os"
	"strings"
	"sync"
	"time"
)

const (
	msgChoices                     = "([r]ock, [p]aper or [s]cissors)"
	msgChoose                      = "Make your choice! " + msgChoices
	msgNoPrivateChat               = "I have lost track of our private chat. Please write me in a private chat and try again"
	msgAlreadyIngame               = "You are already in a game right now, finish that first."
	msgAlreadyIngameWillRemainOpen = msgAlreadyIngame + " Game will remain open..."
	msgGameOpened                  = "Game opened! Join with /join, abort with /abort"
	msgGameStarted                 = "Game started! Waiting for your choices in our private chats..."
	msgGameAborted                 = "Game aborted"
	msgOnlyCreatorCanAbort         = "Only the creator can abort a game"
	msgCreatorAlreadyInGame        = "The creator is already in the game, idiot"
)

type command string

const (
	cmdStart   = command("start")
	cmdStop    = command("stop")
	cmdNew     = command("new")
	cmdJoin    = command("join")
	cmdAbort   = command("abort")
	cmdUnknown = command("unknown")
)

func parseCommand(text string) command {
	text = strings.ToLower(text)
	if !strings.HasPrefix(text, "/") {
		return cmdUnknown
	}

	text = text[1:]
	switch text {
	case "new", "n", "new@" + api.Username:
		return cmdNew
	case "join", "j", "join@" + api.Username:
		return cmdJoin
	case "abort", "a", "abort@" + api.Username:
		return cmdAbort
	case "start", "start@" + api.Username:
		return cmdStart
	case "stop", "stop@" + api.Username:
		return cmdStop
	default:
		return cmdUnknown
	}
}

type choice string

const (
	choiceRock     = choice("rock")
	choicePaper    = choice("paper")
	choiceScissors = choice("scissors")
	choiceUnknown  = choice("unknown")
)

func parseChoice(text string) choice {
	text = strings.ToLower(text)
	switch text {
	case "rock", "r":
		return choiceRock
	case "paper", "p":
		return choicePaper
	case "scissors", "s":
		return choiceScissors
	default:
		return choiceUnknown
	}
}

var api *tbotapi.TelegramBotAPI

// RunBot runs a bot.
// It will block until either something very bad happens or closing is closed.
func RunBot(apiKey string, closing chan struct{}) {
	var err error

	fmt.Println("Starting...")
	if fileExists("chats.json") {
		fmt.Println("Loading chats...")
		err := loadChatsFromFile("chats.json")
		if err != nil {
			fmt.Println("Could not load:", err.Error())
		}
	}

	fmt.Println("Starting bot...")
	api, err = tbotapi.New(apiKey)
	if err != nil {
		log.Fatal(err)
	}

	// just to show its working
	fmt.Printf("User ID: %d\n", api.ID)
	fmt.Printf("Bot Name: %s\n", api.Name)
	fmt.Printf("Bot Username: %s\n", api.Username)

	closed := make(chan struct{})
	wg := &sync.WaitGroup{}

	wg.Add(1)
	go func() {
		defer wg.Done()
		for {
			select {
			case <-closed:
				return
			case update := <-api.Updates:
				if update.Error() != nil {
					fmt.Printf("Update error: %s\n", update.Error())
					continue
				}

				upd := update.Update()
				switch upd.Type() {
				case tbotapi.MessageUpdate:
					handleMessage(*update.Update().Message, api)
				case tbotapi.InlineQueryUpdate, tbotapi.ChosenInlineResultUpdate:
				// ignore
				default:
					fmt.Printf("Ignoring unknown Update type.")
				}
			}
		}
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		for {
			select {
			case <-closed:
				return
			case <-time.After(10 * time.Minute):
				fmt.Println("Saving db...")
				err = dumpChatsToFile("chats.json")
				if err != nil {
					fmt.Println("Could not save:", err.Error())
				}
			}
		}
	}()

	// wait for the signal
	<-closing
	fmt.Println("Closing...")

	fmt.Println("Saving db...")
	err = dumpChatsToFile("chats.json")
	if err != nil {
		fmt.Println("Could not save:", err.Error())
	}

	fmt.Println("Closing bot...")
	// always close the API first, let it clean up the update loop
	api.Close() //this might take a while
	close(closed)
	wg.Wait()
}

func fileExists(filename string) bool {
	fi, err := os.Lstat(filename)
	if fi != nil || (err != nil && !os.IsNotExist(err)) {
		return true
	}
	return false
}

func handleMessage(msg tbotapi.Message, api *tbotapi.TelegramBotAPI) {
	typ := msg.Type()
	if typ != tbotapi.TextMessage {
		//ignore non-text messages for now
		return
	}
	text := *msg.Text
	if msg.Chat.IsPrivateChat() {
		fmt.Printf("<-%d, %s,\t%q\n", msg.ID, msg.Chat, text)
	} else {
		fmt.Printf("<-%d, %s(%s),\t%q\n", msg.ID, msg.Chat, msg.From, text)
	}

	if msg.Chat.IsPrivateChat() {
		//always update the list of private chats
		putChat(msg.From.ID, msg.Chat.ID)
	}

	if strings.HasPrefix(text, "/") {
		//command
		cmd := parseCommand(text)
		if cmd == cmdNew {
			game(msg, api)
			return
		}

		groupsLock.RLock()
		if c, ok := groups[msg.Chat.ID]; ok {
			c <- msg
		}
		groupsLock.RUnlock()
	} else {
		if msg.Chat.IsPrivateChat() {
			uid := msg.From.ID
			expectsLock.Lock()
			if expect, ok := expects[uid]; ok {
				switch parseChoice(text) {
				case choiceRock, choicePaper, choiceScissors:
					expect <- parseChoice(text)
					delete(expects, uid)
				default:
					reply(msg, api, "No understand")
				}
			}
			expectsLock.Unlock()
		}
	}
}

func game(msg tbotapi.Message, api *tbotapi.TelegramBotAPI) {
	if hasExpect(msg.From.ID) {
		reply(msg, api, msgAlreadyIngame)
		return
	}

	if msg.Chat.IsPrivateChat() {
		reply(msg, api, "You will play against the bot. "+msgChoose)

		eChan := make(chan choice)
		expectsLock.Lock()
		expects[msg.From.ID] = eChan
		expectsLock.Unlock()

		go func(original tbotapi.Message, api *tbotapi.TelegramBotAPI, expected chan choice) {
			choice := <-eChan

			botChoice := rand.Float64()

			var resp string
			if botChoice < (float64(1) / float64(3)) {
				resp = formatResponse("the bot", "you", choiceRock, choice)
			} else if botChoice < (float64(2) / float64(3)) {
				resp = formatResponse("the bot", "you", choicePaper, choice)
			} else {
				resp = formatResponse("the bot", "you", choiceScissors, choice)
			}

			reply(original, api, resp)
		}(msg, api, eChan)

	} else {
		//group mode

		if !hasChat(msg.From.ID) {
			reply(msg, api, msgNoPrivateChat)
			return
		}

		if hasGroup(msg.Chat.ID) {
			reply(msg, api, "This group already has an open game.")
			return
		}

		messages := make(chan tbotapi.Message)
		groups[msg.Chat.ID] = messages
		reply(msg, api, msgGameOpened)

		go func(original tbotapi.Message, api *tbotapi.TelegramBotAPI, messages chan tbotapi.Message) {
			var p1, p2 chan choice
			var partner tbotapi.User

		loop:
			for {
				msg := <-messages
				if msg.Type() != tbotapi.TextMessage {
					continue
				}
				text := *msg.Text
				switch parseCommand(text) {
				case cmdJoin:
					if msg.From.ID == original.From.ID {
						reply(original, api, msgCreatorAlreadyInGame)
					} else {
						expectsLock.Lock()
						if _, ok := expects[original.From.ID]; ok {
							reply(msg, api, msgAlreadyIngameWillRemainOpen)
							expectsLock.Unlock()
							continue loop
						}

						if _, ok := expects[msg.From.ID]; ok {
							reply(msg, api, msgAlreadyIngameWillRemainOpen)
							expectsLock.Unlock()
							continue loop
						}

						if !hasChat(msg.From.ID) {
							reply(msg, api, msgNoPrivateChat)
							expectsLock.Unlock()
							continue loop
						}

						groupsLock.Lock()
						delete(groups, original.Chat.ID)
						groupsLock.Unlock()
						p1 = make(chan choice)
						expects[original.From.ID] = p1
						p2 = make(chan choice)
						expects[msg.From.ID] = p2
						expectsLock.Unlock()

						p1Chat := chats[original.From.ID]
						p2Chat := chats[msg.From.ID]

						partner = msg.From

						sendTo(p1Chat, api, msgChoose)
						sendTo(p2Chat, api, msgChoose)
						reply(original, api, msgGameStarted)

						break loop
					}
				case cmdAbort:
					if msg.From.ID == original.From.ID {
						groupsLock.Lock()
						delete(groups, original.Chat.ID)
						groupsLock.Unlock()
						reply(original, api, msgGameAborted)
						return
					} else {
						reply(original, api, msgOnlyCreatorCanAbort)
					}
				}
			}

			// game running

			var choice1, choice2 choice

		nextloop:
			for {
				select {
				case choice1 = <-p1:
					if choice2 != "" {
						break nextloop
					}
				case choice2 = <-p2:
					if choice1 != "" {
						break nextloop
					}
				}
			}

			resp := formatResponse(original.From.String(), partner.String(), choice1, choice2)

			reply(original, api, resp)

		}(msg, api, messages)
	}
}

func formatResponse(part1, part2 string, choice1, choice2 choice) string {
	var res string
	switch choice1 {
	case choiceRock:
		switch choice2 {
		case choiceRock:
			res = "tie"
		case choicePaper:
			res = part2 + " wins"
		case choiceScissors:
			res = part1 + " wins"
		}
	case choicePaper:
		switch choice2 {
		case choiceRock:
			res = part1 + " wins"
		case choicePaper:
			res = "tie"
		case choiceScissors:
			res = part2 + " wins"
		}
	case choiceScissors:
		switch choice2 {
		case choiceRock:
			res = part2 + " wins"
		case choicePaper:
			res = part1 + " wins"
		case choiceScissors:
			res = "tie"
		}
	}

	return fmt.Sprintf("%s chose %s, %s chose %s: %s", part1, choice1, part2, choice2, res)
}

func sendTo(chat int, api *tbotapi.TelegramBotAPI, text string) error {
	outMsg, err := api.NewOutgoingMessage(tbotapi.NewChatRecipient(chat), text).Send()
	if err != nil {
		return err
	}

	fmt.Printf("->%d, %s,\t%q\n", outMsg.Message.ID, outMsg.Message.Chat, *outMsg.Message.Text)
	return nil
}

func reply(msg tbotapi.Message, api *tbotapi.TelegramBotAPI, text string) error {
	return sendTo(msg.Chat.ID, api, text)
}
<|endoftext|>


<commit_before>// This file guides regeneration of the heterogeneous rx package in this folder.
// The [jig tool](https://github.com/reactivego/jig) will generate rx.go guided
// by the code used in the require function.

// +build ignore

package rx

import "time"

func require() {
	_ = NewSubscriber()
	_ = GoroutineScheduler()
	t := MakeTrampolineScheduler()

	/*
		Observable Types
	*/

	var o Observable
	var b ObservableBool
	var i ObservableInt
	var oo ObservableObservable
	var oa ObservableSlice

	/*
		Observable Create Functions
	*/

	Create(func(Next, Error, Complete, Canceled) {})
	CreateRecursive(func(Next, Error, Complete) {})
	CreateFutureRecursive(time.Millisecond, func(Next, Error, Complete) time.Duration { return time.Millisecond })
	Defer(func() Observable { return o })
	Empty()
	From(1, 2)
	FromChan(make(chan interface{}))
	Of(1,2)
	Interval(time.Millisecond)
	Just(1)
	Never()
	Range(1, 2)
	Start(func() (interface{}, error) { return o, nil })
	Throw(RxError("sad"))
	Ticker(time.Millisecond)
	Timer(time.Millisecond)

	/*
		Observable Operator Methods
	*/
	CombineLatest(o)
	o.CombineLatestWith(o)
	o.CombineLatestMap(func(interface{}) Observable { return nil })
	o.CombineLatestMapTo(o)
	oo.CombineLatestAll()

	Concat(o)
	o.ConcatWith()
	// o.ConcatMap
	// o.ConcatMapTo
	// o.ConcatScan
	// o.ConcatReduce
	oo.ConcatAll()

	o.SwitchMap(func(interface{}) Observable { return o })
	// o.SwitchMapTo
	// o.SwitchScan
	// o.SwitchReduce
	oo.SwitchAll()

	// ExhaustMap
	// ExhaustMapTo
	// ExhaustAll

	Merge(o)
	o.MergeWith()
	o.MergeMap(func(interface{}) Observable { return o })
	// o.MergeMapTo(o)
	oo.MergeAll()

	MergeDelayError(o)
	o.MergeDelayErrorWith()
	//o.MergeDelayErrorMap(func(interface{}) Observable { return o })
	// o.MergeDelayErrorMapTo(o)
	//oo.MergeDelayErrorAll()

	o.WithLatestFrom(o)
	oo.WithLatestFromAll()

	o.All(func(next interface{}) bool { return true })
	o.AsObservableBool()
	o.AsObservableInt()
	b.AsObservable()
	i.AsObservable()
	o.AuditTime(time.Millisecond)
	i.Average()
	o.Catch(o)
	o.CatchError(func(error, Observable) Observable {return o})
	o.Count()
	o.DebounceTime(time.Millisecond)
	o.Delay(time.Millisecond)
	o.Distinct()
	o.Do(func(interface{}) {})
	o.DoOnComplete(func() {})
	o.DoOnError(func(error) {})
	o.ElementAt(0)
	o.Filter(func(interface{}) bool { return true })
	o.Finally(func() {})
	o.First()
	o.IgnoreCompletion()
	o.IgnoreElements()
	o.Last()
	o.Map(func(interface{}) interface{} { return nil })
	i.MapObservable(func(int) Observable { return nil })
	o.MapTo(nil)
	i.Max()
	i.Min()
	o.ObserveOn(func(task func()) { task() })
	o.OnlyBool()
	o.OnlyInt()
	// Passthrough
	o.Reduce(func(acc interface{}, value interface{}) interface{} { return nil }, nil)
	o.Repeat(1)
	o.Retry()
	o.SampleTime(time.Millisecond)
	o.Scan(func(acc interface{}, value interface{}) interface{} { return nil }, nil)
	o.Serialize()
	o.Single()
	o.Skip(1)
	o.SkipLast(1)
	o.StartWith(1)
	o.SubscribeOn(t)
	i.Sum()
	o.Take(1)
	i.Take(1)
	o.TakeLast(1)
	o.TakeUntil(o)
	o.TakeWhile(func(interface{}) bool { return true })
	o.ThrottleTime(time.Millisecond)
	o.TimeInterval()
	o.Timeout(time.Millisecond)
	o.Timestamp()

	/*
		Observable Subscribe Methods
	*/

	// Println
	o.Println()
	b.Println()
	i.Println()
	oa.Println()
	o.Subscribe(func(interface{}, error, bool) {})
	b.Subscribe(func(bool, error, bool) {})
	i.Subscribe(func(int, error, bool) {})
	oa.Subscribe(func([]interface{}, error, bool) {})
	o.ToChan()
	b.ToChan()
	i.ToChan()
	oa.ToChan()
	o.ToSingle()
	b.ToSingle()
	i.ToSingle()
	oa.ToSingle()
	o.ToSlice()
	b.ToSlice()
	i.ToSlice()
	oa.ToSlice()
	o.Wait()
	b.Wait()
	i.Wait()
	oa.Wait()

	Println()

	/*
		Multicast Types, Operator Methods and Connect Method
	*/

	var m Multicaster
	m.Connect()
	m.AutoConnect(0)
	m.RefCount()
	o.Publish()
	o.PublishReplay(0, 0)
}
<commit_msg>Rename 'require' func to 'generate'<commit_after>// This file guides regeneration of the heterogeneous rx package in this folder.
// The [jig tool](https://github.com/reactivego/jig) will generate rx.go guided
// by the code used in the generate function.

// +build ignore

package rx

import "time"

func generate() {
	_ = NewSubscriber()
	_ = GoroutineScheduler()
	t := MakeTrampolineScheduler()

	/*
		Observable Types
	*/

	var o Observable
	var b ObservableBool
	var i ObservableInt
	var oo ObservableObservable
	var oa ObservableSlice

	/*
		Observable Create Functions
	*/

	Create(func(Next, Error, Complete, Canceled) {})
	CreateRecursive(func(Next, Error, Complete) {})
	CreateFutureRecursive(time.Millisecond, func(Next, Error, Complete) time.Duration { return time.Millisecond })
	Defer(func() Observable { return o })
	Empty()
	From(1, 2)
	FromChan(make(chan interface{}))
	Of(1,2)
	Interval(time.Millisecond)
	Just(1)
	Never()
	Range(1, 2)
	Start(func() (interface{}, error) { return o, nil })
	Throw(RxError("sad"))
	Ticker(time.Millisecond)
	Timer(time.Millisecond)

	/*
		Observable Operator Methods
	*/
	CombineLatest(o)
	o.CombineLatestWith(o)
	o.CombineLatestMap(func(interface{}) Observable { return nil })
	o.CombineLatestMapTo(o)
	oo.CombineLatestAll()

	Concat(o)
	o.ConcatWith()
	// o.ConcatMap
	// o.ConcatMapTo
	// o.ConcatScan
	// o.ConcatReduce
	oo.ConcatAll()

	o.SwitchMap(func(interface{}) Observable { return o })
	// o.SwitchMapTo
	// o.SwitchScan
	// o.SwitchReduce
	oo.SwitchAll()

	// ExhaustMap
	// ExhaustMapTo
	// ExhaustAll

	Merge(o)
	o.MergeWith()
	o.MergeMap(func(interface{}) Observable { return o })
	// o.MergeMapTo(o)
	oo.MergeAll()

	MergeDelayError(o)
	o.MergeDelayErrorWith()
	//o.MergeDelayErrorMap(func(interface{}) Observable { return o })
	// o.MergeDelayErrorMapTo(o)
	//oo.MergeDelayErrorAll()

	o.WithLatestFrom(o)
	oo.WithLatestFromAll()

	o.All(func(next interface{}) bool { return true })
	o.AsObservableBool()
	o.AsObservableInt()
	b.AsObservable()
	i.AsObservable()
	o.AuditTime(time.Millisecond)
	i.Average()
	o.Catch(o)
	o.CatchError(func(error, Observable) Observable {return o})
	o.Count()
	o.DebounceTime(time.Millisecond)
	o.Delay(time.Millisecond)
	o.Distinct()
	o.Do(func(interface{}) {})
	o.DoOnComplete(func() {})
	o.DoOnError(func(error) {})
	o.ElementAt(0)
	o.Filter(func(interface{}) bool { return true })
	o.Finally(func() {})
	o.First()
	o.IgnoreCompletion()
	o.IgnoreElements()
	o.Last()
	o.Map(func(interface{}) interface{} { return nil })
	i.MapObservable(func(int) Observable { return nil })
	o.MapTo(nil)
	i.Max()
	i.Min()
	o.ObserveOn(func(task func()) { task() })
	o.OnlyBool()
	o.OnlyInt()
	// Passthrough
	o.Reduce(func(acc interface{}, value interface{}) interface{} { return nil }, nil)
	o.Repeat(1)
	o.Retry()
	o.SampleTime(time.Millisecond)
	o.Scan(func(acc interface{}, value interface{}) interface{} { return nil }, nil)
	o.Serialize()
	o.Single()
	o.Skip(1)
	o.SkipLast(1)
	o.StartWith(1)
	o.SubscribeOn(t)
	i.Sum()
	o.Take(1)
	i.Take(1)
	o.TakeLast(1)
	o.TakeUntil(o)
	o.TakeWhile(func(interface{}) bool { return true })
	o.ThrottleTime(time.Millisecond)
	o.TimeInterval()
	o.Timeout(time.Millisecond)
	o.Timestamp()

	/*
		Observable Subscribe Methods
	*/

	// Println
	o.Println()
	b.Println()
	i.Println()
	oa.Println()
	o.Subscribe(func(interface{}, error, bool) {})
	b.Subscribe(func(bool, error, bool) {})
	i.Subscribe(func(int, error, bool) {})
	oa.Subscribe(func([]interface{}, error, bool) {})
	o.ToChan()
	b.ToChan()
	i.ToChan()
	oa.ToChan()
	o.ToSingle()
	b.ToSingle()
	i.ToSingle()
	oa.ToSingle()
	o.ToSlice()
	b.ToSlice()
	i.ToSlice()
	oa.ToSlice()
	o.Wait()
	b.Wait()
	i.Wait()
	oa.Wait()

	Println()

	/*
		Multicast Types, Operator Methods and Connect Method
	*/

	var m Multicaster
	m.Connect()
	m.AutoConnect(0)
	m.RefCount()
	o.Publish()
	o.PublishReplay(0, 0)
}
<|endoftext|>


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

/*
Package s2 implements types and functions for working with geometry in S² (spherical geometry).

Its related packages, parallel to this one, are s1 (operates on S¹), r1 (operates on ℝ¹)
and r3 (operates on ℝ³).

This package provides types and functions for the S2 cell hierarchy and coordinate systems.
The S2 cell hierarchy is a hierarchical decomposition of the surface of a unit sphere (S²)
into ``cells''; it is highly efficient, scales from continental size to under 1 cm²
and preserves spatial locality (nearby cells have close IDs).

A presentation that gives an overview of S2 is
https://docs.google.com/presentation/d/1Hl4KapfAENAOf4gv-pSngKwvS_jwNVHRPZTTDzXXn6Q/view.
*/
package s2
<commit_msg>s2: Update doc to reference the s2geometry.io site instead of the old google doc.<commit_after>// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/*
Package s2 is a library for working with geometry in S² (spherical geometry).

Its related packages, parallel to this one, are s1 (operates on S¹), r1 (operates on ℝ¹),
r2 (operates on ℝ²) and r3 (operates on ℝ³).

This package provides types and functions for the S2 cell hierarchy and coordinate systems.
The S2 cell hierarchy is a hierarchical decomposition of the surface of a unit sphere (S²)
into ``cells''; it is highly efficient, scales from continental size to under 1 cm²
and preserves spatial locality (nearby cells have close IDs).

More information including an in-depth introduction to S2 can be found on the
S2 website https://s2geometry.io/
*/
package s2
<|endoftext|>


<commit_before>// s3stub is a simple server that will write a local file
// on PUT and retrieve files with GET
package main

import (
	"crypto/sha256"
	"flag"
	"fmt"
	"github.com/gorilla/mux"
	"hash"
	"io"
	"log"
	"net/http"
	"os"
	"path"
)

var Root string

type ReadHasher struct {
	h hash.Hash
	r io.Reader
}

func (rh *ReadHasher) Read(buf []byte) (n int, err error) {
	n, err = rh.r.Read(buf)
	if err != nil {
		return n, err
	}

	rh.h.Write(buf[:n])

	return
}

func (rh *ReadHasher) Sum(b []byte) []byte {
	return rh.h.Sum(b)
}

func main() {
	var bind string

	flag.StringVar(&bind, "b", "127.0.0.1:8000", "The address to bind to")
	flag.StringVar(&Root, "r", "", "The root path of the server")
	flag.Parse()

	if Root == "" {
		fmt.Println("s3stub:")
		flag.PrintDefaults()
		return
	}

	r := mux.NewRouter()
	r.HandleFunc("/{path:.+}", download).Methods("GET")
	r.HandleFunc("/{path:.+}", upload).Methods("PUT")
	r.HandleFunc("/{path:.+}", delete).Methods("DELETE")

	http.Handle("/", r)

	log.Println("Listening on:", bind)
	log.Println("Root:", Root)
	log.Fatal(http.ListenAndServe(bind, nil))
}

func download(w http.ResponseWriter, r *http.Request) {
	log.Println("GET", r.URL.Path)
	filename := path.Join(Root, r.URL.Path)

	fp, err := os.Open(filename)
	if err != nil {
		if os.IsNotExist(err) {
			http.Error(w, err.Error(), 404)
		} else {
			http.Error(w, err.Error(), 500)
		}
		return
	}
	defer fp.Close()

	io.Copy(w, fp)
}

func upload(w http.ResponseWriter, r *http.Request) {
	log.Println("PUT", r.URL.Path)
	filename := path.Join(Root, r.URL.Path)

	os.MkdirAll(path.Dir(filename), 0700)

	fp, err := os.Create(filename)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	defer fp.Close()

	bodyReader := &ReadHasher{h: sha256.New(), r: r.Body}
	io.Copy(fp, bodyReader)
	fmt.Fprintf(w, "%x", bodyReader.Sum(nil))
}

func delete(w http.ResponseWriter, r *http.Request) {
	log.Println("DELETE", r.URL.Path)
	filename := path.Join(Root, r.URL.Path)

	err := os.Remove(filename)
	if err != nil {
		if os.IsNotExist(err) {
			http.Error(w, err.Error(), 404)
		} else {
			fmt.Println("wat")
			http.Error(w, err.Error(), 500)
		}
		return
	}

	w.WriteHeader(204)
}
<commit_msg>Added HEAD method to check for file existence<commit_after>// s3stub is a simple server that will write a local file
// on PUT and retrieve files with GET
package main

import (
	"crypto/sha256"
	"flag"
	"fmt"
	"github.com/gorilla/mux"
	"hash"
	"io"
	"log"
	"net/http"
	"os"
	"path"
)

var Root string

type ReadHasher struct {
	h hash.Hash
	r io.Reader
}

func (rh *ReadHasher) Read(buf []byte) (n int, err error) {
	n, err = rh.r.Read(buf)
	if err != nil {
		return n, err
	}

	rh.h.Write(buf[:n])

	return
}

func (rh *ReadHasher) Sum(b []byte) []byte {
	return rh.h.Sum(b)
}

func main() {
	var bind string

	flag.StringVar(&bind, "b", "127.0.0.1:8000", "The address to bind to")
	flag.StringVar(&Root, "r", "", "The root path of the server")
	flag.Parse()

	if Root == "" {
		fmt.Println("s3stub:")
		flag.PrintDefaults()
		return
	}

	r := mux.NewRouter()
	r.HandleFunc("/{path:.+}", download).Methods("GET")
	r.HandleFunc("/{path:.+}", exists).Methods("HEAD")
	r.HandleFunc("/{path:.+}", upload).Methods("PUT")
	r.HandleFunc("/{path:.+}", delete).Methods("DELETE")

	http.Handle("/", r)

	log.Println("Listening on:", bind)
	log.Println("Root:", Root)
	log.Fatal(http.ListenAndServe(bind, nil))
}

func download(w http.ResponseWriter, r *http.Request) {
	log.Println("GET", r.URL.Path)
	filename := path.Join(Root, r.URL.Path)

	fp, err := os.Open(filename)
	if err != nil {
		if os.IsNotExist(err) {
			http.Error(w, err.Error(), 404)
		} else {
			http.Error(w, err.Error(), 500)
		}
		return
	}
	defer fp.Close()

	io.Copy(w, fp)
}

func exists(w http.ResponseWriter, r *http.Request) {
    log.Println("HEAD", r.URL.Path)
    filename := path.Join(Root, r.URL.Path)

	fp, err := os.Open(filename)
	if err != nil {
		if os.IsNotExist(err) {
			http.Error(w, err.Error(), 404)
		} else {
			http.Error(w, err.Error(), 500)
		}
		return
	}
	defer fp.Close()

	w.WriteHeader(204)
}

func upload(w http.ResponseWriter, r *http.Request) {
	log.Println("PUT", r.URL.Path)
	filename := path.Join(Root, r.URL.Path)

	os.MkdirAll(path.Dir(filename), 0700)

	fp, err := os.Create(filename)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	defer fp.Close()

	bodyReader := &ReadHasher{h: sha256.New(), r: r.Body}
	io.Copy(fp, bodyReader)
	fmt.Fprintf(w, "%x", bodyReader.Sum(nil))
}

func delete(w http.ResponseWriter, r *http.Request) {
	log.Println("DELETE", r.URL.Path)
	filename := path.Join(Root, r.URL.Path)

	err := os.Remove(filename)
	if err != nil {
		if os.IsNotExist(err) {
			http.Error(w, err.Error(), 404)
		} else {
			fmt.Println("wat")
			http.Error(w, err.Error(), 500)
		}
		return
	}

	w.WriteHeader(204)
}
<|endoftext|>


<commit_before>package sansan

import (
	"fmt"
	"sync"
	"sync/atomic"
)

type Program []byte

func (p Program) Run() {
	var wg sync.WaitGroup
	defer wg.Wait()

	wg.Add(1)
	newHeap().run(p, &wg)
}

const heapsize = 30000

type heap struct {
	mem []int32
	pnt int
}

func newHeap() heap {
	return heap{mem: make([]int32, heapsize)}
}

func (h heap) run(p Program, wg *sync.WaitGroup) int {
	defer wg.Done()

	var childWG sync.WaitGroup
	defer childWG.Wait()

	for i := 0; i < len(p); i++ {
		switch ins := p[i]; ins {
		case '>':
			h.pnt++
			h.pnt = (h.pnt%heapsize + heapsize) % heapsize
		case '<':
			h.pnt--
			h.pnt = (h.pnt%heapsize + heapsize) % heapsize
		case '+':
			atomic.AddInt32(&h.mem[h.pnt], 1)
		case '-':
			atomic.AddInt32(&h.mem[h.pnt], -1)
		case '[':
			end := i + findClosing(p[i:])
			if atomic.LoadInt32(&h.mem[h.pnt]) != 0 {
				// enter loop
				childWG.Add(1) // TODO: remove this on loops
				h.pnt = h.run(p[i+1:], &childWG)
			}
			i = end // goto end
		case ']':
			if atomic.LoadInt32(&h.mem[h.pnt]) == 0 {
				return h.pnt
			}
			i = -1

		case '{':
			end := i + findClosing(p[i:])
			childWG.Add(1)
			go h.run(p[i+1:], &childWG)

			i = end // continue parrent thread
		case '}':
			return -1 // kill thread

		case '.':
			fmt.Printf("%c", atomic.LoadInt32(&h.mem[h.pnt]))
		case ',':
			var n int32
			if _, err := fmt.Scanf("%d\n", &n); err != nil {
				panic(err)
			}
			atomic.SwapInt32(&h.mem[h.pnt], n)
		case ' ', '\t', '\n':
		default:
		}
	}
	return -1
}

func findClosing(prog []byte) int {
	braces := 0
	for i := 0; i < len(prog); i++ {
		switch prog[i] {
		case '[', '{':
			braces++
		case ']', '}':
			braces--
			if braces < 0 {
				panic("invalid program: unbalanced braces")
			}
			if braces == 0 {
				return i
			}
		}
	}
	panic("invalid program: could not find closing ']'")
}
<commit_msg>remove heap pointer as a return value<commit_after>package sansan

import (
	"fmt"
	"sync"
	"sync/atomic"
)

type Program []byte

func (p Program) Run() {
	var wg sync.WaitGroup
	defer wg.Wait()

	wg.Add(1)
	newHeap().run(p, &wg)
}

const heapsize = 30000

type heap struct {
	mem []int32
	pnt int
}

func newHeap() *heap {
	return &heap{mem: make([]int32, heapsize)}
}

func (h *heap) run(p Program, wg *sync.WaitGroup) {
	defer wg.Done()

	var childWG sync.WaitGroup
	defer childWG.Wait()

	for i := 0; i < len(p); i++ {
		switch ins := p[i]; ins {
		case '>':
			h.pnt++
			h.pnt = (h.pnt%heapsize + heapsize) % heapsize
		case '<':
			h.pnt--
			h.pnt = (h.pnt%heapsize + heapsize) % heapsize
		case '+':
			atomic.AddInt32(&h.mem[h.pnt], 1)
		case '-':
			atomic.AddInt32(&h.mem[h.pnt], -1)
		case '[':
			end := i + findClosing(p[i:])
			if atomic.LoadInt32(&h.mem[h.pnt]) != 0 {
				// enter loop
				childWG.Add(1) // TODO: remove this on loops
				h.run(p[i+1:], &childWG)
			}
			i = end // goto end
		case ']':
			if atomic.LoadInt32(&h.mem[h.pnt]) == 0 {
				return
			}
			i = -1

		case '{':
			end := i + findClosing(p[i:])

			newH := *h // copy heap mem and pointer
			childWG.Add(1)
			go newH.run(p[i+1:], &childWG)

			i = end // continue parrent thread
		case '}':
			return // kill thread

		case '.':
			fmt.Printf("%c", atomic.LoadInt32(&h.mem[h.pnt]))
		case ',':
			var n int32
			if _, err := fmt.Scanf("%d\n", &n); err != nil {
				panic(err)
			}
			atomic.SwapInt32(&h.mem[h.pnt], n)
		case ' ', '\t', '\n':
		default:
		}
	}
}

func findClosing(prog []byte) int {
	braces := 0
	for i := 0; i < len(prog); i++ {
		switch prog[i] {
		case '[', '{':
			braces++
		case ']', '}':
			braces--
			if braces < 0 {
				panic("invalid program: unbalanced braces")
			}
			if braces == 0 {
				return i
			}
		}
	}
	panic("invalid program: could not find closing ']'")
}
<|endoftext|>


<commit_before>//Package sbinfo reads information from a filesystem's superblock and returns a
// struct representing that superblock. At the moment only works with ext2/3/4.
package sbinfo

import (
	"fmt"
	"os"
	"unsafe"
)

const ext2Magic uint16 = 0xef53

// ReadExt2Superblock reads the superblock from the given device or file and
// returns a struct representing the superblock, or an error on failure.
func ReadExt2Superblock(devPath string) (*Ext2Sb, error) {
	fp, err := os.Open(devPath)
	if err != nil {
		return nil, err
	}
	// read in the first 2k bytes
	buf := make([]byte, 2048)
	n, err := fp.Read(buf)
	if n != 2048 {
		nerr := fmt.Errorf("Expected 2048 bytes, only read %d from %s", n, devPath)
		return nil, nerr
	}
	if err != nil {
		return nil, err
	}
	sbRaw := buf[1024:]
	sb := *(*Ext2Sb)(unsafe.Pointer(&sbRaw[0]))
	if sb.SMagic != ext2Magic {
		sbErr := fmt.Errorf("Bad magic number for %s", devPath)
		return nil, err
	}
	return &sb, nil
}
<commit_msg>... and return the right error. Geez.<commit_after>//Package sbinfo reads information from a filesystem's superblock and returns a
// struct representing that superblock. At the moment only works with ext2/3/4.
package sbinfo

import (
	"fmt"
	"os"
	"unsafe"
)

const ext2Magic uint16 = 0xef53

// ReadExt2Superblock reads the superblock from the given device or file and
// returns a struct representing the superblock, or an error on failure.
func ReadExt2Superblock(devPath string) (*Ext2Sb, error) {
	fp, err := os.Open(devPath)
	if err != nil {
		return nil, err
	}
	// read in the first 2k bytes
	buf := make([]byte, 2048)
	n, err := fp.Read(buf)
	if n != 2048 {
		nerr := fmt.Errorf("Expected 2048 bytes, only read %d from %s", n, devPath)
		return nil, nerr
	}
	if err != nil {
		return nil, err
	}
	sbRaw := buf[1024:]
	sb := *(*Ext2Sb)(unsafe.Pointer(&sbRaw[0]))
	if sb.SMagic != ext2Magic {
		sbErr := fmt.Errorf("Bad magic number for %s", devPath)
		return nil, sbErr
	}
	return &sb, nil
}
<|endoftext|>


<commit_before>package chevalier

import (
	"encoding/json"
	"github.com/mattbaird/elastigo/api"
	es "github.com/mattbaird/elastigo/core"
	"github.com/mattbaird/elastigo/search"
	"log"
	"fmt"
	"strings"
	"time"
)

type QueryEngine struct {
	indexName      string
	dataType       string
	nSources       int
	updateInterval time.Duration
}

func NewQueryEngine(host, indexName, dataType string) *QueryEngine {
	e := new(QueryEngine)
	e.indexName = indexName
	e.dataType = dataType
	api.Domain = host
	e.updateSourceCount()
	e.updateInterval = time.Second * 10
	go e.updateForever()
	return e
}

func (e *QueryEngine) sanitizeField(f string) string {
	f = strings.TrimSpace(f)
	if f == "*" {
		return fmt.Sprintf("%s._all", e.dataType)
	}
	f = fmt.Sprintf("%s.%s", e.dataType, f)
	// Need to escape wildcards here for some reason.
	f = strings.Replace(f, "*", "\\*", -1)
	return f
}

func (e *QueryEngine) buildTagQuery(tag *SourceRequest_Tag) *search.QueryDsl {
	qs := new(search.QueryString)
	qs.Fields = make([]string, 0)
	qs.Fields = append(qs.Fields, e.sanitizeField(*tag.Field))
	qs.Query = *tag.Value
	q := search.Query().Qs(qs)
	return q
}

type SourceQuery map[string]interface{}

func (e *QueryEngine) BuildQuery(req *SourceRequest) SourceQuery {
	_ = search.Search(e.indexName).Type(e.dataType)
	tags := req.GetTags()
	tagQueries := make([]*search.QueryDsl, len(tags))
	for i, tag := range tags {
		tagQueries[i] = e.buildTagQuery(tag)
	}
	query := map[string]interface{}{
		"query": map[string]interface{}{
			"bool": map[string]interface{}{
				"must": tagQueries,
			},
		},
	}
	return SourceQuery(query)
}

func (e *QueryEngine) runSourceRequest(req *SourceRequest) (*es.SearchResult, error) {
	q := e.BuildQuery(req)
	res, err := es.SearchRequest(false, e.indexName, e.dataType, q, "", 0)
	return &res, err
}

func (e *QueryEngine) GetSources(req *SourceRequest) (*DataSourceBurst, error) {
	res, err := e.runSourceRequest(req)
	if err != nil {
		return nil, err
	}
	sources := make([]*DataSource, len(res.Hits.Hits))
	for i, hit := range res.Hits.Hits {
		source := new(ElasticsearchSource)
		err = json.Unmarshal(hit.Source, source)
		if err != nil {
			return nil, err
		}
		sources[i] = source.Unmarshal()
	}
	burst := BuildSourceBurst(sources)
	return burst, nil
}

func FmtResult(result *es.SearchResult) []string {
	results := make([]string, len(result.Hits.Hits))
	for i, hit := range result.Hits.Hits {
		results[i] = string(hit.Source[:])
	}
	return results
}

func (e *QueryEngine) updateSourceCount() error {
	resp, err := es.Count(false, e.indexName, e.dataType)
	e.nSources = resp.Count
	return err
}

func (e *QueryEngine) updateForever() {
	for true {
		time.Sleep(e.updateInterval)
		err := e.updateSourceCount()
		if err != nil {
			log.Printf("Error updating source count: %v", err)
		}
	}
}
<commit_msg>Document some things<commit_after>package chevalier

import (
	"encoding/json"
	"github.com/mattbaird/elastigo/api"
	es "github.com/mattbaird/elastigo/core"
	"github.com/mattbaird/elastigo/search"
	"log"
	"fmt"
	"strings"
	"time"
)

// QueryEngine presents an interface for running queries for sources
// against Elasticsearch.
type QueryEngine struct {
	indexName      string
	dataType       string
	nSources       int
	updateInterval time.Duration
}

// NewQueryEngine initializes a QueryEngine with the supplied
// Elasticsearch metadata. indexName and dataType can be anything as
// long as they're consistent.
func NewQueryEngine(host, indexName, dataType string) *QueryEngine {
	e := new(QueryEngine)
	e.indexName = indexName
	e.dataType = dataType
	api.Domain = host
	e.updateSourceCount()
	e.updateInterval = time.Second * 10
	go e.updateForever()
	return e
}

// sanitizeField takes an input field specifier (from a
// SourceRequest_Tag) and munges it so Elasticsearch likes it more. In
// particular, it makes single-wildcard queries work correctly.
//
// FIXME: mid-field wildcards still aren't working.
func (e *QueryEngine) sanitizeField(f string) string {
	f = strings.TrimSpace(f)
	if f == "*" {
		return fmt.Sprintf("%s._all", e.dataType)
	}
	f = fmt.Sprintf("%s.%s", e.dataType, f)
	// Need to escape wildcards here for some reason.
	f = strings.Replace(f, "*", "\\*", -1)
	return f
}

// buildTagQuery constructs an elastigo query object (search.QueryDsl)
// from a SourceRequest_Tag, designed to be plugged into a
// query-string-type[0] query later on.
//
// [0]: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html
func (e *QueryEngine) buildTagQuery(tag *SourceRequest_Tag) *search.QueryDsl {
	qs := new(search.QueryString)
	qs.Fields = make([]string, 0)
	qs.Fields = append(qs.Fields, e.sanitizeField(*tag.Field))
	qs.Query = *tag.Value
	q := search.Query().Qs(qs)
	return q
}

type SourceQuery map[string]interface{}

// BuildQuery takes a SourceRequest and turns it into a multi-level
// map suitable for marshalling to JSON and sending to Elasticsearch.
func (e *QueryEngine) BuildQuery(req *SourceRequest) SourceQuery {
	_ = search.Search(e.indexName).Type(e.dataType)
	tags := req.GetTags()
	tagQueries := make([]*search.QueryDsl, len(tags))
	for i, tag := range tags {
		tagQueries[i] = e.buildTagQuery(tag)
	}
	query := map[string]interface{}{
		"query": map[string]interface{}{
			"bool": map[string]interface{}{
				"must": tagQueries,
			},
		},
	}
	return SourceQuery(query)
}

// runSourceRequest takes a request object and returns an elastigo-type
// (i.e., intermediate) result.
func (e *QueryEngine) runSourceRequest(req *SourceRequest) (*es.SearchResult, error) {
	q := e.BuildQuery(req)
	res, err := es.SearchRequest(false, e.indexName, e.dataType, q, "", 0)
	return &res, err
}

// GetSources takes a request object and returns the DataSourceBurst of
// the sources it gets back from Elasticsearch.
func (e *QueryEngine) GetSources(req *SourceRequest) (*DataSourceBurst, error) {
	res, err := e.runSourceRequest(req)
	if err != nil {
		return nil, err
	}
	sources := make([]*DataSource, len(res.Hits.Hits))
	for i, hit := range res.Hits.Hits {
		source := new(ElasticsearchSource)
		err = json.Unmarshal(hit.Source, source)
		if err != nil {
			return nil, err
		}
		sources[i] = source.Unmarshal()
	}
	burst := BuildSourceBurst(sources)
	return burst, nil
}

// FmtResult returns a string from a SearchResult by interpreting it in
// the most naive manner possible. For debugging.
func FmtResult(result *es.SearchResult) []string {
	results := make([]string, len(result.Hits.Hits))
	for i, hit := range result.Hits.Hits {
		results[i] = string(hit.Source[:])
	}
	return results
}

// updateSourceCount updates our running total of documents-in-index
// (by asking Elasticsearch).
func (e *QueryEngine) updateSourceCount() error {
	resp, err := es.Count(false, e.indexName, e.dataType)
	e.nSources = resp.Count
	return err
}

// updateForever updates the source counter on a regular basis.
func (e *QueryEngine) updateForever() {
	for true {
		time.Sleep(e.updateInterval)
		err := e.updateSourceCount()
		if err != nil {
			log.Printf("Error updating source count: %v", err)
		}
	}
}
<|endoftext|>


<commit_before>package store

import "io"

// Searcher is an interface for the different searcher params
type Searcher interface {
	// expr returns a sqlite snippet used in the WHERE clause.
	Expr() string
	// params returns the required parameters for the sqlite snippet.
	Params() []interface{}
}

// between is a searcher which searches for values between (inclusive) the
// given values.
type between struct {
	col      string
	from, to int
}

// Between returns a Searcher that looks for an integer between two values
func Between(column string, from, to int) Searcher {
	return between{column, from, to}
}

func (b between) Expr() string {
	return "[" + b.col + "] BETWEEN ? AND ?"
}

func (b between) Params() []interface{} {
	return []interface{}{b.from, b.to}
}

// like implements a search which uses the LIKE syntax in a WHERE clause.
type like struct {
	col, likeStr string
}

// Like returns a Searcher that looks for similar strings
func Like(column, likeStr string) Searcher {
	return like{column, likeStr}
}

func (l like) Expr() string {
	return "[" + l.col + "] LIKE ?"
}

func (l like) Params() []interface{} {
	return []interface{}{l.likeStr}
}

type notLike struct {
	col, notLikeStr string
}

// NotLike returns a Searcher that excludes results with similar strings
func NotLike(column, notLikeStr string) Searcher {
	return notLike{column, notLikeStr}
}

func (n notLike) Expr() string {
	return "[" + n.col + "] NOT LIKE ?"
}

func (n notLike) Params() []interface{} {
	return []interface{}{n.notLikeStr}
}

type matchString struct {
	col, match string
}

// MatchString searches for an exact (case-insensitive) string match on the given column
func MatchString(column, match string) Searcher {
	return matchString{column, match}
}

func (m matchString) Expr() string {
	return "[" + m.col + "] = ?"
}

func (m matchString) Params() []interface{} {
	return []interface{}{m.match}
}

type matchInt struct {
	col   string
	match int
}

// MatchInt searches for an exact integer match on the given column
func MatchInt(column string, match int) Searcher {
	return matchInt{column, match}
}

func (m matchInt) Expr() string {
	return "[" + m.col + "] = ?"
}

func (m matchInt) Params() []interface{} {
	return []interface{}{m.match}
}

type or []Searcher

// Or returns a Searcher that matches for any of the given searchers
func Or(searchers ...Searcher) Searcher {
	return or(searchers)
}

func (o or) Expr() string {
	expr := "( "
	for n, searcher := range o {
		if n > 0 {
			expr += " OR "
		}
		expr += searcher.Expr()
	}
	expr += " )"
	return expr
}

func (o or) Params() []interface{} {
	p := make([]interface{}, 0, len(o))
	for _, searcher := range o {
		p = append(p, searcher.Params())
	}
	return p
}

type and []Searcher

// And returns a Search that matches all of the given searches. Useful for
// within an Or Searcher
func And(searchers ...Searcher) Searcher {
	return and(searchers)
}

func (a and) Expr() string {
	expr := "( "
	for n, searcher := range a {
		if n > 0 {
			expr += " AND "
		}
		expr += searcher.Expr()
	}
	expr += " )"
	return expr
}

func (a and) Params() []interface{} {
	p := make([]interface{}, 0, len(a))
	for _, searcher := range a {
		p = append(p, searcher.Params())
	}
	return p
}

// Search is used for a custom (non primary key) search on a table.
//
// Returns the number of items found and an error if any occurred.
func (s *Store) Search(data []Interface, offset int, params ...Searcher) (int, error) {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	if len(data) == 0 {
		return 0, nil
	}
	table := tableName(data[0])
	cols := data[0].Get()
	vars := make([]string, 0, len(cols))
	first := true
	columns := ""
	for col := range cols {
		if first {
			first = false
		} else {
			columns += ", "
		}
		columns += "[" + col + "]"
		vars = append(vars, col)
	}
	var clause string
	paramVars := make([]interface{}, 0, len(params)+2)
	if len(params) > 0 {
		clause = "WHERE "
		first = true
		for _, param := range params {
			if first {
				first = false
			} else {
				clause += " AND "
			}
			clause += param.Expr()
			paramVars = append(paramVars, param.Params()...)
		}
	}
	var sortBy, dir string
	if s, ok := data[0].(sort); ok {
		sortBy = s.sortBy
		if s.asc {
			dir = "ASC"
		} else {
			dir = "DESC"
		}
	} else {
		sortBy = data[0].Key()
		dir = "ASC"
	}
	sql := "SELECT " + columns + " FROM [" + table + "] " + clause + " ORDER BY [" + sortBy + "] " + dir + " LIMIT ? OFFSET ?;"
	stmt, err := s.db.Query(sql, append(paramVars, len(data), offset)...)
	stmtVars := statement{Stmt: stmt, vars: vars}
	pos := 0
	for ; err == nil; err = stmt.Next() {
		if typeName := tableName(data[pos]); typeName != table {
			err = UnmatchedType{table, typeName}
		} else {
			err = stmtVars.Scan(stmtVars.Vars(data[pos].Get())...)
			pos++
		}
	}
	if err != nil && err != io.EOF {
		return pos, err
	}

	return pos, nil
}

// SearchCount performs the search and returns the number of results
func (s *Store) SearchCount(data Interface, params ...Searcher) (int, error) {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	table := tableName(data)
	first := true
	var clause string
	paramVars := make([]interface{}, 0, len(params))
	if len(params) > 0 {
		clause = "WHERE "
		for _, param := range params {
			if first {
				first = false
			} else {
				clause += " AND "
			}
			clause += param.Expr()
			paramVars = append(paramVars, param.Params()...)
		}
	}
	sql := "SELECT count(1) FROM [" + table + "] " + clause + ";"
	stmt, err := s.db.Query(sql, paramVars...)
	if err != nil {
		return 0, err
	}
	count := 0
	err = stmt.Scan(&count)
	if err != nil && err != io.EOF {
		return 0, err
	}
	return count, nil
}

//Errors

// UnknownColumn is an error that occurrs when a search parameter requires a
// column which does not exist for the given type.
type UnknownColumn string

func (u UnknownColumn) Error() string {
	return "unknown column: " + string(u)
}
<commit_msg>Corrected param collection<commit_after>package store

import "io"

// Searcher is an interface for the different searcher params
type Searcher interface {
	// expr returns a sqlite snippet used in the WHERE clause.
	Expr() string
	// params returns the required parameters for the sqlite snippet.
	Params() []interface{}
}

// between is a searcher which searches for values between (inclusive) the
// given values.
type between struct {
	col      string
	from, to int
}

// Between returns a Searcher that looks for an integer between two values
func Between(column string, from, to int) Searcher {
	return between{column, from, to}
}

func (b between) Expr() string {
	return "[" + b.col + "] BETWEEN ? AND ?"
}

func (b between) Params() []interface{} {
	return []interface{}{b.from, b.to}
}

// like implements a search which uses the LIKE syntax in a WHERE clause.
type like struct {
	col, likeStr string
}

// Like returns a Searcher that looks for similar strings
func Like(column, likeStr string) Searcher {
	return like{column, likeStr}
}

func (l like) Expr() string {
	return "[" + l.col + "] LIKE ?"
}

func (l like) Params() []interface{} {
	return []interface{}{l.likeStr}
}

type notLike struct {
	col, notLikeStr string
}

// NotLike returns a Searcher that excludes results with similar strings
func NotLike(column, notLikeStr string) Searcher {
	return notLike{column, notLikeStr}
}

func (n notLike) Expr() string {
	return "[" + n.col + "] NOT LIKE ?"
}

func (n notLike) Params() []interface{} {
	return []interface{}{n.notLikeStr}
}

type matchString struct {
	col, match string
}

// MatchString searches for an exact (case-insensitive) string match on the given column
func MatchString(column, match string) Searcher {
	return matchString{column, match}
}

func (m matchString) Expr() string {
	return "[" + m.col + "] = ?"
}

func (m matchString) Params() []interface{} {
	return []interface{}{m.match}
}

type matchInt struct {
	col   string
	match int
}

// MatchInt searches for an exact integer match on the given column
func MatchInt(column string, match int) Searcher {
	return matchInt{column, match}
}

func (m matchInt) Expr() string {
	return "[" + m.col + "] = ?"
}

func (m matchInt) Params() []interface{} {
	return []interface{}{m.match}
}

type or []Searcher

// Or returns a Searcher that matches for any of the given searchers
func Or(searchers ...Searcher) Searcher {
	return or(searchers)
}

func (o or) Expr() string {
	expr := "( "
	for n, searcher := range o {
		if n > 0 {
			expr += " OR "
		}
		expr += searcher.Expr()
	}
	expr += " )"
	return expr
}

func (o or) Params() []interface{} {
	p := make([]interface{}, 0, len(o))
	for _, searcher := range o {
		p = append(p, searcher.Params()...)
	}
	return p
}

type and []Searcher

// And returns a Search that matches all of the given searches. Useful for
// within an Or Searcher
func And(searchers ...Searcher) Searcher {
	return and(searchers)
}

func (a and) Expr() string {
	expr := "( "
	for n, searcher := range a {
		if n > 0 {
			expr += " AND "
		}
		expr += searcher.Expr()
	}
	expr += " )"
	return expr
}

func (a and) Params() []interface{} {
	p := make([]interface{}, 0, len(a))
	for _, searcher := range a {
		p = append(p, searcher.Params()...)
	}
	return p
}

// Search is used for a custom (non primary key) search on a table.
//
// Returns the number of items found and an error if any occurred.
func (s *Store) Search(data []Interface, offset int, params ...Searcher) (int, error) {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	if len(data) == 0 {
		return 0, nil
	}
	table := tableName(data[0])
	cols := data[0].Get()
	vars := make([]string, 0, len(cols))
	first := true
	columns := ""
	for col := range cols {
		if first {
			first = false
		} else {
			columns += ", "
		}
		columns += "[" + col + "]"
		vars = append(vars, col)
	}
	var clause string
	paramVars := make([]interface{}, 0, len(params)+2)
	if len(params) > 0 {
		clause = "WHERE "
		first = true
		for _, param := range params {
			if first {
				first = false
			} else {
				clause += " AND "
			}
			clause += param.Expr()
			paramVars = append(paramVars, param.Params()...)
		}
	}
	var sortBy, dir string
	if s, ok := data[0].(sort); ok {
		sortBy = s.sortBy
		if s.asc {
			dir = "ASC"
		} else {
			dir = "DESC"
		}
	} else {
		sortBy = data[0].Key()
		dir = "ASC"
	}
	sql := "SELECT " + columns + " FROM [" + table + "] " + clause + " ORDER BY [" + sortBy + "] " + dir + " LIMIT ? OFFSET ?;"
	stmt, err := s.db.Query(sql, append(paramVars, len(data), offset)...)
	stmtVars := statement{Stmt: stmt, vars: vars}
	pos := 0
	for ; err == nil; err = stmt.Next() {
		if typeName := tableName(data[pos]); typeName != table {
			err = UnmatchedType{table, typeName}
		} else {
			err = stmtVars.Scan(stmtVars.Vars(data[pos].Get())...)
			pos++
		}
	}
	if err != nil && err != io.EOF {
		return pos, err
	}

	return pos, nil
}

// SearchCount performs the search and returns the number of results
func (s *Store) SearchCount(data Interface, params ...Searcher) (int, error) {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	table := tableName(data)
	first := true
	var clause string
	paramVars := make([]interface{}, 0, len(params))
	if len(params) > 0 {
		clause = "WHERE "
		for _, param := range params {
			if first {
				first = false
			} else {
				clause += " AND "
			}
			clause += param.Expr()
			paramVars = append(paramVars, param.Params()...)
		}
	}
	sql := "SELECT count(1) FROM [" + table + "] " + clause + ";"
	stmt, err := s.db.Query(sql, paramVars...)
	if err != nil {
		return 0, err
	}
	count := 0
	err = stmt.Scan(&count)
	if err != nil && err != io.EOF {
		return 0, err
	}
	return count, nil
}

//Errors

// UnknownColumn is an error that occurrs when a search parameter requires a
// column which does not exist for the given type.
type UnknownColumn string

func (u UnknownColumn) Error() string {
	return "unknown column: " + string(u)
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"errors"
	"fmt"
	"sort"
)

const (
	evalInf = 50000
)

var (
	errCheckmate checkmateError
	errStalemate = errors.New("stalemate")
	errFiftyMove = errors.New("fifty-move rule")
	// TODO: threefold repetition
)

// A checkmateError indicates the number of plies until a forced checkmate can be delivered.
// The zero value of type checkmateError indicates that the current position is checkmate.
type checkmateError int

func (n checkmateError) Error() string {
	if n&1 == 0 {
		return fmt.Sprintf("-#%d", n/2)
	}
	return fmt.Sprintf("#%d", (n+1)/2)
}

// Prev returns the checkmateError corresponding to the previous ply.
func (n checkmateError) Prev() checkmateError { return n + 1 }

// SearchPosition searches a Position to the specified depth via iterative deepening
// and returns the search results.
func SearchPosition(ctx context.Context, pos Position, depth int) Results {
	var results Results
	s := Search{
		allowCutoff: true,
		counters:    make([]int, depth+1),
	}
	for d := 1; d <= depth; d++ {
		_, results, _ = s.negamax(ctx, pos, results, Window{-evalInf, evalInf}, d)
		if ctx.Err() != nil {
			break
		}
	}
	return results
}

type Search struct {
	allowCutoff bool
	counters    []int
}

// negamax recursively searches a Position to the specified depth and returns the evaluation score
// relative to the side to move and the search results. It employs alpha-beta pruning outside of
// the specified Window. If recommended is zero length, negamax will generate and search all legal
// moves; if recommended moves are provided, they must all be legal, and only they will be searched.
func (s *Search) negamax(
	ctx context.Context,
	pos Position,
	recommended Results,
	w Window,
	depth int,
) (bestScore RelScore, results Results, err error) {
	s.counters[len(s.counters)-1-depth]++

	score, recommended, err := checkDone(pos, recommended)
	if err != nil && (s.allowCutoff || err != errInsufficient) {
		// Do not cut off during perft in the case of insufficient material
		return score, nil, err
	}
	// Invariant: len(recommended) > 0 and recommended contains only legal moves
	if depth == 0 {
		score, err := Eval(pos)
		return score.Rel(pos.ToMove), nil, err
	}

	results = recommended
	defer func() { results.SortFor(pos.ToMove) }()

	for _, r := range recommended {
		if r.err != nil && s.allowCutoff {
			// The move is already known not to avoid a game-ending state; no need to search it further
			results = results.Update(r)
			continue
		}

		score, cont, err := s.negamax(ctx, Make(pos, r.move), r.cont, w.Neg(), depth-1)
		score *= -1

		results = results.Update(Result{move: r.move, score: score.Abs(pos.ToMove), depth: depth - 1, cont: cont, err: err})

		if depth >= 3 && ctx.Err() != nil {
			break
		}

		var constrained, ok bool
		w, constrained, ok = w.Constrain(score)
		if constrained {
			// improved lower bound
		}
		if !ok && s.allowCutoff {
			break
		}
	}
	if err, ok := results[0].err.(checkmateError); ok {
		return w.alpha, results, err.Prev()
	}
	return w.alpha, results, results[0].err
}

// checkDone reports whether pos represents a game-ending position.
// If so, the RelScore and error value indicate the type of ending.
// If not, the RelScore is 0 and the error is nil.
// In either case, checkDone returns a Results of all legal moves in pos.
// If recommended is not provided, checkDone generates the legal moves first.
func checkDone(pos Position, recommended Results) (RelScore, Results, error) {
	if len(recommended) == 0 {
		moves := Candidates(pos) // pseudo-legal
		recommended = make(Results, 0, len(moves))
		for _, m := range moves {
			if !IsLegal(Make(pos, m)) {
				continue
			}
			recommended = append(recommended, Result{move: m})
		}
	}
	if len(recommended) == 0 {
		// no legal moves
		if IsCheck(pos) {
			return -evalInf, nil, errCheckmate
		}
		return 0, nil, errStalemate
	}
	if pos.HalfMove == 100 {
		// fifty-move rule
		return 0, nil, errFiftyMove
	}
	return 0, recommended, nil
}

// IsPseudoLegal returns whether a Move is pseudo-legal in a Position.
// A move is pseudo-legal if the square to be moved from contains the specified piece
// and the piece is capable of moving to the target square if doing so would not put the king in check.
func IsPseudoLegal(pos Position, move Move) bool {
	for _, m := range PieceMoves[move.Piece](pos) {
		if m == move {
			return true
		}
	}
	return false
}

// IsLegal returns whether a Position is legal.
// A position is illegal if the king of the side that just moved is in check.
func IsLegal(pos Position) bool {
	return !IsAttacked(pos, pos.KingSquare[pos.Opp()], pos.ToMove)
}

// IsCheck returns whether the king of the side to move is in check.
func IsCheck(pos Position) bool {
	return IsAttacked(pos, pos.KingSquare[pos.ToMove], pos.Opp())
}

// IsTerminal returns whether or not a Position is checkmate or stalemate.
// A position is checkmate or stalemate if the side to move has no legal moves.
func IsTerminal(pos Position) bool { return !anyLegal(pos, Candidates(pos)) }

// anyLegal returns whether any of the given Moves are legal in the given Position.
func anyLegal(pos Position, moves []Move) bool {
	for _, m := range moves {
		if IsLegal(Make(pos, m)) {
			return true
		}
	}
	return false
}

// A Result holds a searched Move along with its evaluated Score,
// the search depth, and the continuation Results of the search.
type Result struct {
	move  Move
	score Score
	depth int
	cont  Results
	err   error
}

// String returns a string representation of r, including its principal variation.
func (r Result) String() string {
	s := fmt.Sprintf("%v (%v) %v", r.score, r.depth, r.PV())
	if r.err != nil {
		return s + " " + r.err.Error()
	}
	return s
}

// PV returns a string representation of r's principal variation.
func (r Result) PV() string {
	if r.depth == 0 || len(r.cont) == 0 {
		return LongAlgebraic(r.move)
	}
	return LongAlgebraic(r.move) + " " + r.cont[0].PV()
}

// A Results contains the results of a search.
// Functions returning a Results should sort it before returning.
type Results []Result

// SortFor sorts rs beginning with the best move for c.
// The sort is not guaranteed to be stable.
func (rs Results) SortFor(c Color) {
	// Sort first by mate condition, then by depth decreasing,
	// then by Score increasing/decreasing for White/Black,
	// and then by origin and destination Square increasing
	sort.Slice(rs, func(i, j int) bool {
		if less, ok := rs.mateSort(i, j); ok {
			return less
		}
		if rs[i].depth != rs[j].depth {
			return rs[i].depth > rs[j].depth
		}
		if rs[i].score != rs[j].score {
			return (rs[i].score > rs[j].score) == (c == White)
		}
		return rs.squareSort(i, j)
	})
}

// SortBySquares sorts rs first by the Result moves' origin Squares and then by their destination Squares,
// and finally by promotion piece in the case of pawn promotions.
func (rs Results) SortBySquares() { sort.Slice(rs, rs.squareSort) }

// mateSort reports how two Result elements should be sorted if at least one of them leads to checkmate.
// ok reports whether either Result contains an error of type checkmateError.
// If ok is true, less reports whether the Result with index i should sort before the Result with index j.
// If ok is false, the value of less is meaningless.
func (rs Results) mateSort(i, j int) (less bool, ok bool) {
	ich, iok := rs[i].err.(checkmateError)
	jch, jok := rs[j].err.(checkmateError)
	switch {
	case iok && jok:
		switch iwin, jwin := ich&1 == 0, jch&1 == 0; {
		case iwin && jwin:
			return ich < jch, true
		case !iwin && !jwin:
			return ich > jch, true
		default:
			return iwin, true
		}
	case iok:
		return ich&1 == 0, true
	case jok:
		return jch&1 != 0, true
	}
	return false, false
}

// squareSort reports how two Result elements should be sorted by their moves' origin and destination Squares,
// and then finally by promotion piece in the case of promoting pawns.
func (rs Results) squareSort(i, j int) bool {
	if rs[i].move.From != rs[j].move.From {
		return rs[i].move.From < rs[j].move.From
	}
	if rs[i].move.To != rs[j].move.To {
		return rs[i].move.To < rs[j].move.To
	}
	// From and To are non-unique in the case of pawn promotion
	return rs[i].move.PromotePiece < rs[j].move.PromotePiece
}

// Update looks for a Result in rs with the same Move as r and replaces it with r,
// or appends r if there is none, and returns the possibly updated slice.
func (rs Results) Update(r Result) Results {
	for i := range rs {
		if rs[i].move == r.move {
			rs[i] = r
			return rs
		}
	}
	return append(rs, r)
}

// String returns a string representation of all Result values in r.
func (rs Results) String() string {
	var s string
	for _, r := range rs {
		s += fmt.Sprintf("%v\n", r.String())
	}
	return s
}

// A Window represents the bounds of a position's evaluation.
type Window struct{ alpha, beta RelScore }

// Constrain updates the lower bound of w, if applicable, and returns the updated window,
// whether the lower bound was changed, and whether the returned Window remains valid.
func (w Window) Constrain(n RelScore) (c Window, constrained bool, ok bool) {
	if n <= w.alpha {
		return w, false, true
	}
	return Window{n, w.beta}, true, n <= w.beta
}

// Neg returns the additive inverse of w.
func (w Window) Neg() Window { return Window{-w.beta, -w.alpha} }
<commit_msg>simplify signature of Update<commit_after>package main

import (
	"context"
	"errors"
	"fmt"
	"sort"
)

const (
	evalInf = 50000
)

var (
	errCheckmate checkmateError
	errStalemate = errors.New("stalemate")
	errFiftyMove = errors.New("fifty-move rule")
	// TODO: threefold repetition
)

// A checkmateError indicates the number of plies until a forced checkmate can be delivered.
// The zero value of type checkmateError indicates that the current position is checkmate.
type checkmateError int

func (n checkmateError) Error() string {
	if n&1 == 0 {
		return fmt.Sprintf("-#%d", n/2)
	}
	return fmt.Sprintf("#%d", (n+1)/2)
}

// Prev returns the checkmateError corresponding to the previous ply.
func (n checkmateError) Prev() checkmateError { return n + 1 }

// SearchPosition searches a Position to the specified depth via iterative deepening
// and returns the search results.
func SearchPosition(ctx context.Context, pos Position, depth int) Results {
	var results Results
	s := Search{
		allowCutoff: true,
		counters:    make([]int, depth+1),
	}
	for d := 1; d <= depth; d++ {
		_, results, _ = s.negamax(ctx, pos, results, Window{-evalInf, evalInf}, d)
		if ctx.Err() != nil {
			break
		}
	}
	return results
}

type Search struct {
	allowCutoff bool
	counters    []int
}

// negamax recursively searches a Position to the specified depth and returns the evaluation score
// relative to the side to move and the search results. It employs alpha-beta pruning outside of
// the specified Window. If recommended is zero length, negamax will generate and search all legal
// moves; if recommended moves are provided, they must all be legal, and only they will be searched.
func (s *Search) negamax(
	ctx context.Context,
	pos Position,
	recommended Results,
	w Window,
	depth int,
) (bestScore RelScore, results Results, err error) {
	s.counters[len(s.counters)-1-depth]++

	score, recommended, err := checkDone(pos, recommended)
	if err != nil && (s.allowCutoff || err != errInsufficient) {
		// Do not cut off during perft in the case of insufficient material
		return score, nil, err
	}
	// Invariant: len(recommended) > 0 and recommended contains only legal moves
	if depth == 0 {
		score, err := Eval(pos)
		return score.Rel(pos.ToMove), nil, err
	}

	results = recommended
	defer func() { results.SortFor(pos.ToMove) }()

	for _, r := range recommended {
		if r.err != nil && s.allowCutoff {
			// The move is already known not to avoid a game-ending state; no need to search it further
			results.Update(r)
			continue
		}

		score, cont, err := s.negamax(ctx, Make(pos, r.move), r.cont, w.Neg(), depth-1)
		score *= -1

		results.Update(Result{move: r.move, score: score.Abs(pos.ToMove), depth: depth - 1, cont: cont, err: err})

		if depth >= 3 && ctx.Err() != nil {
			break
		}

		var constrained, ok bool
		w, constrained, ok = w.Constrain(score)
		if constrained {
			// improved lower bound
		}
		if !ok && s.allowCutoff {
			break
		}
	}
	if err, ok := results[0].err.(checkmateError); ok {
		return w.alpha, results, err.Prev()
	}
	return w.alpha, results, results[0].err
}

// checkDone reports whether pos represents a game-ending position.
// If so, the RelScore and error value indicate the type of ending.
// If not, the RelScore is 0 and the error is nil.
// In either case, checkDone returns a Results of all legal moves in pos.
// If recommended is not provided, checkDone generates the legal moves first.
func checkDone(pos Position, recommended Results) (RelScore, Results, error) {
	if len(recommended) == 0 {
		moves := Candidates(pos) // pseudo-legal
		recommended = make(Results, 0, len(moves))
		for _, m := range moves {
			if !IsLegal(Make(pos, m)) {
				continue
			}
			recommended = append(recommended, Result{move: m})
		}
	}
	if len(recommended) == 0 {
		// no legal moves
		if IsCheck(pos) {
			return -evalInf, nil, errCheckmate
		}
		return 0, nil, errStalemate
	}
	if pos.HalfMove == 100 {
		// fifty-move rule
		return 0, nil, errFiftyMove
	}
	return 0, recommended, nil
}

// IsPseudoLegal returns whether a Move is pseudo-legal in a Position.
// A move is pseudo-legal if the square to be moved from contains the specified piece
// and the piece is capable of moving to the target square if doing so would not put the king in check.
func IsPseudoLegal(pos Position, move Move) bool {
	for _, m := range PieceMoves[move.Piece](pos) {
		if m == move {
			return true
		}
	}
	return false
}

// IsLegal returns whether a Position is legal.
// A position is illegal if the king of the side that just moved is in check.
func IsLegal(pos Position) bool {
	return !IsAttacked(pos, pos.KingSquare[pos.Opp()], pos.ToMove)
}

// IsCheck returns whether the king of the side to move is in check.
func IsCheck(pos Position) bool {
	return IsAttacked(pos, pos.KingSquare[pos.ToMove], pos.Opp())
}

// IsTerminal returns whether or not a Position is checkmate or stalemate.
// A position is checkmate or stalemate if the side to move has no legal moves.
func IsTerminal(pos Position) bool { return !anyLegal(pos, Candidates(pos)) }

// anyLegal returns whether any of the given Moves are legal in the given Position.
func anyLegal(pos Position, moves []Move) bool {
	for _, m := range moves {
		if IsLegal(Make(pos, m)) {
			return true
		}
	}
	return false
}

// A Result holds a searched Move along with its evaluated Score,
// the search depth, and the continuation Results of the search.
type Result struct {
	move  Move
	score Score
	depth int
	cont  Results
	err   error
}

// String returns a string representation of r, including its principal variation.
func (r Result) String() string {
	s := fmt.Sprintf("%v (%v) %v", r.score, r.depth, r.PV())
	if r.err != nil {
		return s + " " + r.err.Error()
	}
	return s
}

// PV returns a string representation of r's principal variation.
func (r Result) PV() string {
	if r.depth == 0 || len(r.cont) == 0 {
		return LongAlgebraic(r.move)
	}
	return LongAlgebraic(r.move) + " " + r.cont[0].PV()
}

// A Results contains the results of a search.
// Functions returning a Results should sort it before returning.
type Results []Result

// SortFor sorts rs beginning with the best move for c.
// The sort is not guaranteed to be stable.
func (rs Results) SortFor(c Color) {
	// Sort first by mate condition, then by depth decreasing,
	// then by Score increasing/decreasing for White/Black,
	// and then by origin and destination Square increasing
	sort.Slice(rs, func(i, j int) bool {
		if less, ok := rs.mateSort(i, j); ok {
			return less
		}
		if rs[i].depth != rs[j].depth {
			return rs[i].depth > rs[j].depth
		}
		if rs[i].score != rs[j].score {
			return (rs[i].score > rs[j].score) == (c == White)
		}
		return rs.squareSort(i, j)
	})
}

// SortBySquares sorts rs first by the Result moves' origin Squares and then by their destination Squares,
// and finally by promotion piece in the case of pawn promotions.
func (rs Results) SortBySquares() { sort.Slice(rs, rs.squareSort) }

// mateSort reports how two Result elements should be sorted if at least one of them leads to checkmate.
// ok reports whether either Result contains an error of type checkmateError.
// If ok is true, less reports whether the Result with index i should sort before the Result with index j.
// If ok is false, the value of less is meaningless.
func (rs Results) mateSort(i, j int) (less bool, ok bool) {
	ich, iok := rs[i].err.(checkmateError)
	jch, jok := rs[j].err.(checkmateError)
	switch {
	case iok && jok:
		switch iwin, jwin := ich&1 == 0, jch&1 == 0; {
		case iwin && jwin:
			return ich < jch, true
		case !iwin && !jwin:
			return ich > jch, true
		default:
			return iwin, true
		}
	case iok:
		return ich&1 == 0, true
	case jok:
		return jch&1 != 0, true
	}
	return false, false
}

// squareSort reports how two Result elements should be sorted by their moves' origin and destination Squares,
// and then finally by promotion piece in the case of promoting pawns.
func (rs Results) squareSort(i, j int) bool {
	if rs[i].move.From != rs[j].move.From {
		return rs[i].move.From < rs[j].move.From
	}
	if rs[i].move.To != rs[j].move.To {
		return rs[i].move.To < rs[j].move.To
	}
	// From and To are non-unique in the case of pawn promotion
	return rs[i].move.PromotePiece < rs[j].move.PromotePiece
}

// Update finds the Result in rs with the same Move as r and replaces it with r.
// It panics if rs does not contain any elements with r's Move.
func (rs Results) Update(r Result) {
	for i := range rs {
		if rs[i].move == r.move {
			rs[i] = r
			return
		}
	}
	// rs should contain all legal moves
	panic("unreached")
}

// String returns a string representation of all Result values in r.
func (rs Results) String() string {
	var s string
	for _, r := range rs {
		s += fmt.Sprintf("%v\n", r.String())
	}
	return s
}

// A Window represents the bounds of a position's evaluation.
type Window struct{ alpha, beta RelScore }

// Constrain updates the lower bound of w, if applicable, and returns the updated window,
// whether the lower bound was changed, and whether the returned Window remains valid.
func (w Window) Constrain(n RelScore) (c Window, constrained bool, ok bool) {
	if n <= w.alpha {
		return w, false, true
	}
	return Window{n, w.beta}, true, n <= w.beta
}

// Neg returns the additive inverse of w.
func (w Window) Neg() Window { return Window{-w.beta, -w.alpha} }
<|endoftext|>


<commit_before>package uniform

import (
	"bytes"
	"encoding/xml"
)

type SelectChild interface{}

type Select struct {
	Options []SelectChild
}

type OptGroup struct {
	Label    string
	Disabled bool
	Options  []Option
}

type Option struct {
	Value    string
	Text     string
	Selected bool
	Disabled bool
}

func (s Select) String() (string, error) {
	buffer := bytes.NewBuffer(nil)
	encoder := errEncoder{encoder: xml.NewEncoder(buffer)}

	encoder.encodeToken(xml.StartElement{
		Name: xml.Name{Local: "select"},
	})

	for _, child := range s.Options {
		switch child.(type) {
		case OptGroup:
			optGroup := child.(OptGroup)

			optGroupAttrs := make(attributes, 0)
			optGroupAttrs.addString("label", optGroup.Label)
			optGroupAttrs.addBoolean("disabled", optGroup.Disabled)

			encoder.encodeToken(xml.StartElement{
				Name: xml.Name{Local: "optgroup"},
				Attr: optGroupAttrs.xmlAttr(),
			})

			for _, option := range optGroup.Options {
				encodeOption(encoder, option)
			}

			encoder.encodeToken(xml.EndElement{Name: xml.Name{Local: "optgroup"}})
		case Option:
			encodeOption(encoder, child.(Option))
		}
	}

	encoder.encodeToken(xml.EndElement{Name: xml.Name{Local: "select"}})
	encoder.flush()

	if encoder.err != nil {
		return "", encoder.err
	}

	return buffer.String(), nil
}

func encodeOption(encoder errEncoder, option Option) {
	optionAttrs := make(attributes, 0)
	optionAttrs.addString("value", option.Value)
	optionAttrs.addBoolean("selected", option.Selected)
	optionAttrs.addBoolean("disabled", option.Disabled)

	encoder.encodeToken(xml.StartElement{
		Name: xml.Name{Local: "option"},
		Attr: optionAttrs.xmlAttr(),
	})

	if option.Text != "" {
		encoder.encodeToken(xml.CharData(option.Text))
	}

	encoder.encodeToken(xml.EndElement{Name: xml.Name{Local: "option"}})
}
<commit_msg>Extract helper method for encoding OptGroups<commit_after>package uniform

import (
	"bytes"
	"encoding/xml"
)

type SelectChild interface{}

type Select struct {
	Options []SelectChild
}

type OptGroup struct {
	Label    string
	Disabled bool
	Options  []Option
}

type Option struct {
	Value    string
	Text     string
	Selected bool
	Disabled bool
}

func (s Select) String() (string, error) {
	buffer := bytes.NewBuffer(nil)
	encoder := errEncoder{encoder: xml.NewEncoder(buffer)}

	encoder.encodeToken(xml.StartElement{
		Name: xml.Name{Local: "select"},
	})

	for _, child := range s.Options {
		switch child.(type) {
		case OptGroup:
			encodeOptGroup(encoder, child.(OptGroup))
		case Option:
			encodeOption(encoder, child.(Option))
		}
	}

	encoder.encodeToken(xml.EndElement{Name: xml.Name{Local: "select"}})
	encoder.flush()

	if encoder.err != nil {
		return "", encoder.err
	}

	return buffer.String(), nil
}

func encodeOptGroup(encoder errEncoder, optGroup OptGroup) {
	optGroupAttrs := make(attributes, 0)
	optGroupAttrs.addString("label", optGroup.Label)
	optGroupAttrs.addBoolean("disabled", optGroup.Disabled)

	encoder.encodeToken(xml.StartElement{
		Name: xml.Name{Local: "optgroup"},
		Attr: optGroupAttrs.xmlAttr(),
	})

	for _, option := range optGroup.Options {
		encodeOption(encoder, option)
	}

	encoder.encodeToken(xml.EndElement{Name: xml.Name{Local: "optgroup"}})
}

func encodeOption(encoder errEncoder, option Option) {
	optionAttrs := make(attributes, 0)
	optionAttrs.addString("value", option.Value)
	optionAttrs.addBoolean("selected", option.Selected)
	optionAttrs.addBoolean("disabled", option.Disabled)

	encoder.encodeToken(xml.StartElement{
		Name: xml.Name{Local: "option"},
		Attr: optionAttrs.xmlAttr(),
	})

	if option.Text != "" {
		encoder.encodeToken(xml.CharData(option.Text))
	}

	encoder.encodeToken(xml.EndElement{Name: xml.Name{Local: "option"}})
}
<|endoftext|>


<commit_before>// Google Cloud Messaging for application servers implemented using the
// Go programming language.
package gcm

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"math/rand"
	"net/http"
	"strconv"
	"time"
)

const (
	// GcmSendEndpoint is the endpoint for sending messages to the GCM server.
	GcmSendEndpoint = "https://android.googleapis.com/gcm/send"
	// Initial delay (ms) before first retry, without jitter.
	backoffInitialDelay = 1000
	// Maximum delay (ms) before a retry.
	maxBackoffDelay = 1024000
	// Percentage jitter to use when retrying.  Jitter is a random variation in
	// timing to prevent many retries from hitting the server simulatenously.
	// Recommended values are between 0 and 50
	jitterPercentage = 50
)

// Declared as a mutable variable for testing purposes.
var gcmSendEndpoint = GcmSendEndpoint

// // Errors
// type JSONParseError struct{ error }
// type UnauthorizedError struct{ error }
// type UnknownError struct{ error }

const ()

// Sender abstracts the interaction between the application server and the
// GCM server. The developer must obtain an API key from the Google APIs
// Console page and pass it to the Sender so that it can perform authorized
// requests on the application server's behalf. To send a message to one or
// more devices use the Sender's Send or SendNoRetry methods.
//
// If the HTTP field is nil, a zeroed http.Client will be allocated and used
// to send messages. If your application server runs on Google AppEngine,
// you must use the "appengine/urlfetch" package to create the *http.Client
// as follows:
//
//	func handler(w http.ResponseWriter, r *http.Request) {
//		c := appengine.NewContext(r)
//		client := urlfetch.Client(c)
//		sender := &gcm.Sender{APIKey: key, HTTP: client}
//
//		/* ... */
//	}
type Sender struct {
	APIKey string
	HTTP   *http.Client
}

// HTTPError is a custom error type to handle returning errors with GCM
// http response codes
type HTTPError struct {
	StatusCode int
	Err        error
	RetryAfter int
}

func (r *HTTPError) Error() string {
	return fmt.Sprintf("%d error: %s\nretry-after: %d", r.StatusCode, r.Err, r.RetryAfter)
}

// SendNoRetry sends a message to the GCM server without retrying in case of
// service unavailability. A non-nil error is returned if a non-recoverable
// error occurs (i.e. if the response status is not "200 OK").
func (s *Sender) SendNoRetry(msg *Message) (*Response, *HTTPError) {
	if err := checkSender(s); err != nil {
		return nil, &HTTPError{Err: err}
	} else if err := checkMessage(msg); err != nil {
		return nil, &HTTPError{Err: err}
	}

	data, err := json.Marshal(msg)
	if err != nil {
		return nil, &HTTPError{Err: err}
	}

	req, err := http.NewRequest("POST", gcmSendEndpoint, bytes.NewBuffer(data))
	if err != nil {
		return nil, &HTTPError{Err: err}
	}
	req.Header.Add("Authorization", fmt.Sprintf("key=%s", s.APIKey))
	req.Header.Add("Content-Type", "application/json")

	resp, err := s.HTTP.Do(req)
	defer resp.Body.Close()
	if err != nil {
		return nil, &HTTPError{Err: err}
	}

	// if the status is not StatusOK, return with the error
	if resp.StatusCode != http.StatusOK {
		delta, err := parseRetryAfter(resp.Header.Get("Retry-After"))
		if err != nil {
			return nil, &HTTPError{
				StatusCode: resp.StatusCode,
				Err:        errors.New(resp.Status),
			}
		}
		return nil, &HTTPError{
			StatusCode: resp.StatusCode,
			Err:        errors.New(resp.Status),
			RetryAfter: delta,
		}
	}

	// get the body and decode
	response := new(Response)
	err = json.NewDecoder(resp.Body).Decode(response)
	if err != nil {
		return response, &HTTPError{Err: err}
	}
	return response, nil
}

// parseRetryAfter attempts to parse the contents of the Retry-After http
// header.  It returns the time in seconds if successful, and an error for
// either an unparseable value or a nil value.
func parseRetryAfter(r string) (int, error) {
	// if not set
	if r == "" {
		return 0, errors.New("Empty 'Retry-After' header")
	}

	// if set as an integer delta (assumed to be in seconds)
	if delta, err := strconv.Atoi(r); err == nil {
		return max(delta, 0), err
	}

	// if set as a date, convert to a time in seconds
	if ra, err := time.Parse(time.RFC1123, r); err == nil {
		delta := (ra.UnixNano() - time.Now().UnixNano()) / time.Second
		return delta, err
	}
	fmt.Printf("#936r Unparseable 'Retry-After' header: %s", r)
	return 0, errors.New("Unparseable 'Retry-After' header: %s", r)
}

// Send sends a message to the GCM server, retrying in case of service
// unavailability. A non-nil error is returned if a non-recoverable
// error occurs (i.e. if the response status is not "200 OK").
//
// Note that messages are retried using exponential backoff, and as a
// result, this method may block for several seconds.
func (s *Sender) Send(msg *Message, retries int) (*Response, *HTTPError) {
	if retries < 0 {
		return nil, &HTTPError{Err: errors.New("'retries' must be positive")}
	}

	// Send the message for the first time.
	resp, err := s.SendNoRetry(msg)
	if err != nil {
		return nil, &HTTPError{Err: err}
	}

	// if there were no errors, or retries = 0, return the result
	if resp.Failure == 0 || retries == 0 {
		return resp, err // err should always be nil here
	}

	// One or more messages failed to send.
	regIDs := msg.RegistrationIDs // store the original RegistrationIDs
	allResults := make(map[string]Result, len(regIDs))
	backoff := backoffInitialDelay
	for i := 0; updateStatus(msg, resp, allResults) > 0 && i < retries; i++ {
		sleepTime := calculateSleep(backoff)
		time.Sleep(time.Duration(sleepTime) * time.Millisecond)
		backoff = min(2*backoff, maxBackoffDelay)
		if resp, err = s.SendNoRetry(msg); err != nil {
			msg.RegistrationIDs = regIDs
			return nil, &HTTPError{Err: err}
		}
	}

	// Bring the message back to its original state.
	msg.RegistrationIDs = regIDs

	// Create a Response containing the overall results.
	finalResults := make([]Result, len(regIDs))
	var success, failure, canonicalIDs int
	for i := 0; i < len(regIDs); i++ {
		result, _ := allResults[regIDs[i]]
		finalResults[i] = result
		if result.MessageID != "" {
			if result.RegistrationID != "" {
				canonicalIDs++
			}
			success++
		} else {
			failure++
		}
	}

	return &Response{
		// Return the most recent multicast id.
		MulticastID:  resp.MulticastID,
		Success:      success,
		Failure:      failure,
		CanonicalIDs: canonicalIDs,
		Results:      finalResults,
	}, nil
}

func calculateSleep(backoff int) int {
	return (backoff*(100-jitterPercentage))/100 + rand.Intn((2*backoff*jitterPercentage)/100)
}

// updateStatus updates the status of the messages sent to devices and
// returns the number of recoverable errors that could be retried.
func updateStatus(msg *Message, resp *Response, allResults map[string]Result) int {
	unsentRegIDs := make([]string, 0, resp.Failure)
	for i := 0; i < len(resp.Results); i++ {
		regID := msg.RegistrationIDs[i]
		allResults[regID] = resp.Results[i]
		if resp.Results[i].Error == "Unavailable" {
			unsentRegIDs = append(unsentRegIDs, regID)
		}
	}
	msg.RegistrationIDs = unsentRegIDs
	return len(unsentRegIDs)
}

// min returns the smaller of two integers. For exciting religious wars
// about why this wasn't included in the "math" package, see this thread:
// https://groups.google.com/d/topic/golang-nuts/dbyqx_LGUxM/discussion
func min(a, b int) int {
	if a <= b {
		return a
	}
	return b
}

func max(a, b int) int {
	if a >= b {
		return a
	}
	return b
}

// checkSender returns an error if the sender is not well-formed and
// initializes a zeroed http.Client if one has not been provided.
func checkSender(sender *Sender) error {
	if sender.APIKey == "" {
		return errors.New("the sender's API key must not be empty")
	}
	if sender.HTTP == nil {
		sender.HTTP = new(http.Client)
	}
	return nil
}

// checkMessage returns an error if the message is not well-formed.
func checkMessage(msg *Message) error {
	if msg == nil {
		return errors.New("the message must not be nil")
	} else if msg.RegistrationIDs == nil {
		return errors.New("the message's RegistrationIDs field must not be nil")
	} else if len(msg.RegistrationIDs) == 0 {
		return errors.New("the message must specify at least one registration ID")
	} else if len(msg.RegistrationIDs) > 1000 {
		return errors.New("the message may specify at most 1000 registration IDs")
	} else if msg.TimeToLive < 0 || 2419200 < msg.TimeToLive {
		return errors.New("the message's TimeToLive field must be an integer " +
			"between 0 and 2419200 (4 weeks)")
	}
	return nil
}
<commit_msg>fix error with times<commit_after>// Google Cloud Messaging for application servers implemented using the
// Go programming language.
package gcm

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"math/rand"
	"net/http"
	"strconv"
	"time"
)

const (
	// GcmSendEndpoint is the endpoint for sending messages to the GCM server.
	GcmSendEndpoint = "https://android.googleapis.com/gcm/send"
	// Initial delay (ms) before first retry, without jitter.
	backoffInitialDelay = 1000
	// Maximum delay (ms) before a retry.
	maxBackoffDelay = 1024000
	// Percentage jitter to use when retrying.  Jitter is a random variation in
	// timing to prevent many retries from hitting the server simulatenously.
	// Recommended values are between 0 and 50
	jitterPercentage = 50
)

// Declared as a mutable variable for testing purposes.
var gcmSendEndpoint = GcmSendEndpoint

// // Errors
// type JSONParseError struct{ error }
// type UnauthorizedError struct{ error }
// type UnknownError struct{ error }

const ()

// Sender abstracts the interaction between the application server and the
// GCM server. The developer must obtain an API key from the Google APIs
// Console page and pass it to the Sender so that it can perform authorized
// requests on the application server's behalf. To send a message to one or
// more devices use the Sender's Send or SendNoRetry methods.
//
// If the HTTP field is nil, a zeroed http.Client will be allocated and used
// to send messages. If your application server runs on Google AppEngine,
// you must use the "appengine/urlfetch" package to create the *http.Client
// as follows:
//
//	func handler(w http.ResponseWriter, r *http.Request) {
//		c := appengine.NewContext(r)
//		client := urlfetch.Client(c)
//		sender := &gcm.Sender{APIKey: key, HTTP: client}
//
//		/* ... */
//	}
type Sender struct {
	APIKey string
	HTTP   *http.Client
}

// HTTPError is a custom error type to handle returning errors with GCM
// http response codes
type HTTPError struct {
	StatusCode int
	Err        error
	RetryAfter int
}

func (r *HTTPError) Error() string {
	return fmt.Sprintf("%d error: %s\nretry-after: %d", r.StatusCode, r.Err, r.RetryAfter)
}

// SendNoRetry sends a message to the GCM server without retrying in case of
// service unavailability. A non-nil error is returned if a non-recoverable
// error occurs (i.e. if the response status is not "200 OK").
func (s *Sender) SendNoRetry(msg *Message) (*Response, *HTTPError) {
	if err := checkSender(s); err != nil {
		return nil, &HTTPError{Err: err}
	} else if err := checkMessage(msg); err != nil {
		return nil, &HTTPError{Err: err}
	}

	data, err := json.Marshal(msg)
	if err != nil {
		return nil, &HTTPError{Err: err}
	}

	req, err := http.NewRequest("POST", gcmSendEndpoint, bytes.NewBuffer(data))
	if err != nil {
		return nil, &HTTPError{Err: err}
	}
	req.Header.Add("Authorization", fmt.Sprintf("key=%s", s.APIKey))
	req.Header.Add("Content-Type", "application/json")

	resp, err := s.HTTP.Do(req)
	defer resp.Body.Close()
	if err != nil {
		return nil, &HTTPError{Err: err}
	}

	// if the status is not StatusOK, return with the error
	if resp.StatusCode != http.StatusOK {
		delta, err := parseRetryAfter(resp.Header.Get("Retry-After"))
		if err != nil {
			return nil, &HTTPError{
				StatusCode: resp.StatusCode,
				Err:        errors.New(resp.Status),
			}
		}
		return nil, &HTTPError{
			StatusCode: resp.StatusCode,
			Err:        errors.New(resp.Status),
			RetryAfter: delta,
		}
	}

	// get the body and decode
	response := new(Response)
	err = json.NewDecoder(resp.Body).Decode(response)
	if err != nil {
		return response, &HTTPError{Err: err}
	}
	return response, nil
}

// parseRetryAfter attempts to parse the contents of the Retry-After http
// header.  It returns the time in seconds if successful, and an error for
// either an unparseable value or a nil value.
func parseRetryAfter(r string) (int, error) {
	// if not set
	if r == "" {
		return 0, fmt.Errorf("Empty Retry-After header")
	}

	// if set as an integer delta (assumed to be in seconds)
	if delta, err := strconv.Atoi(r); err == nil {
		return max(delta, 0), err
	}

	// if set as a date, convert to a time in seconds
	if ra, err := time.Parse(time.RFC1123, r); err == nil {
		delta := (ra.UnixNano() - time.Now().UnixNano()) / 1e9
		return int(delta), err
	}
	fmt.Printf("#936r Unparseable 'Retry-After' header: %s", r)
	return 0, fmt.Errorf("Unparseable 'Retry-After' header: %s", r)
}

// Send sends a message to the GCM server, retrying in case of service
// unavailability. A non-nil error is returned if a non-recoverable
// error occurs (i.e. if the response status is not "200 OK").
//
// Note that messages are retried using exponential backoff, and as a
// result, this method may block for several seconds.
func (s *Sender) Send(msg *Message, retries int) (*Response, *HTTPError) {
	if retries < 0 {
		return nil, &HTTPError{Err: fmt.Errorf("'retries' must be positive")}
	}

	// Send the message for the first time.
	resp, err := s.SendNoRetry(msg)
	if err != nil {
		return nil, &HTTPError{Err: err}
	}

	// if there were no errors, or retries = 0, return the result
	if resp.Failure == 0 || retries == 0 {
		return resp, err // err should always be nil here
	}

	// One or more messages failed to send.
	regIDs := msg.RegistrationIDs // store the original RegistrationIDs
	allResults := make(map[string]Result, len(regIDs))
	backoff := backoffInitialDelay
	for i := 0; updateStatus(msg, resp, allResults) > 0 && i < retries; i++ {
		sleepTime := calculateSleep(backoff)
		time.Sleep(time.Duration(sleepTime) * time.Millisecond)
		backoff = min(2*backoff, maxBackoffDelay)
		if resp, err = s.SendNoRetry(msg); err != nil {
			msg.RegistrationIDs = regIDs
			return nil, &HTTPError{Err: err}
		}
	}

	// Bring the message back to its original state.
	msg.RegistrationIDs = regIDs

	// Create a Response containing the overall results.
	finalResults := make([]Result, len(regIDs))
	var success, failure, canonicalIDs int
	for i := 0; i < len(regIDs); i++ {
		result, _ := allResults[regIDs[i]]
		finalResults[i] = result
		if result.MessageID != "" {
			if result.RegistrationID != "" {
				canonicalIDs++
			}
			success++
		} else {
			failure++
		}
	}

	return &Response{
		// Return the most recent multicast id.
		MulticastID:  resp.MulticastID,
		Success:      success,
		Failure:      failure,
		CanonicalIDs: canonicalIDs,
		Results:      finalResults,
	}, nil
}

func calculateSleep(backoff int) int {
	return (backoff*(100-jitterPercentage))/100 + rand.Intn((2*backoff*jitterPercentage)/100)
}

// updateStatus updates the status of the messages sent to devices and
// returns the number of recoverable errors that could be retried.
func updateStatus(msg *Message, resp *Response, allResults map[string]Result) int {
	unsentRegIDs := make([]string, 0, resp.Failure)
	for i := 0; i < len(resp.Results); i++ {
		regID := msg.RegistrationIDs[i]
		allResults[regID] = resp.Results[i]
		if resp.Results[i].Error == "Unavailable" {
			unsentRegIDs = append(unsentRegIDs, regID)
		}
	}
	msg.RegistrationIDs = unsentRegIDs
	return len(unsentRegIDs)
}

// min returns the smaller of two integers. For exciting religious wars
// about why this wasn't included in the "math" package, see this thread:
// https://groups.google.com/d/topic/golang-nuts/dbyqx_LGUxM/discussion
func min(a, b int) int {
	if a <= b {
		return a
	}
	return b
}

func max(a, b int) int {
	if a >= b {
		return a
	}
	return b
}

// checkSender returns an error if the sender is not well-formed and
// initializes a zeroed http.Client if one has not been provided.
func checkSender(sender *Sender) error {
	if sender.APIKey == "" {
		return errors.New("the sender's API key must not be empty")
	}
	if sender.HTTP == nil {
		sender.HTTP = new(http.Client)
	}
	return nil
}

// checkMessage returns an error if the message is not well-formed.
func checkMessage(msg *Message) error {
	if msg == nil {
		return errors.New("the message must not be nil")
	} else if msg.RegistrationIDs == nil {
		return errors.New("the message's RegistrationIDs field must not be nil")
	} else if len(msg.RegistrationIDs) == 0 {
		return errors.New("the message must specify at least one registration ID")
	} else if len(msg.RegistrationIDs) > 1000 {
		return errors.New("the message may specify at most 1000 registration IDs")
	} else if msg.TimeToLive < 0 || 2419200 < msg.TimeToLive {
		return errors.New("the message's TimeToLive field must be an integer " +
			"between 0 and 2419200 (4 weeks)")
	}
	return nil
}
<|endoftext|>