<commit_before>// Copyright 2015 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package common

import (
	"encoding/json"
	"fmt"
	"time"
)

const (

	//Entry Credit Blocks (For now, everyone gets the same cap)
	EC_CAP = 5      //Number of ECBlocks we start with.
	AB_CAP = EC_CAP //Administrative Block Cap for AB messages

	//Limits and Sizes
	MAX_ENTRY_SIZE    = uint16(10240) //Maximum size for Entry External IDs and the Data
	HASH_LENGTH       = int(32)       //Length of a Hash
	SIG_LENGTH        = int(64)       //Length of a signature
	MAX_ORPHAN_SIZE   = int(5000)     //Prphan mem pool size
	MAX_TX_POOL_SIZE  = int(50000)    //Transaction mem pool size
	MAX_BLK_POOL_SIZE = int(500000)   //Block mem bool size
	MAX_PLIST_SIZE    = int(150000)   //MY Process List size

	MAX_ENTRY_CREDITS = uint8(10) //Max number of entry credits per entry
	MAX_CHAIN_CREDITS = uint8(20) //Max number of entry credits per chain

	COMMIT_TIME_WINDOW = time.Duration(12) //Time windows for commit chain and commit entry +/- 12 hours

	// maxProtocolVersion is the max protocol version the peer supports.
	//Common constants
	VERSION_0       = byte(0)
	FACTOMD_VERSION = 3005002            //fixed point. resolves to 0.<minor1 release>.<minor2 release>.<minor3 release>
	NETWORK_ID_DB   = uint32(4203931041) //0xFA92E5A1
	NETWORK_ID_EB   = uint32(4203931042) //0xFA92E5A2
	NETWORK_ID_CB   = uint32(4203931043) //0xFA92E5A3

	//For Factom TestNet
	NETWORK_ID_TEST = uint32(0) //0x0

	//Server running mode
	FULL_NODE   = "FULL"
	SERVER_NODE = "SERVER"
	LIGHT_NODE  = "LIGHT"

	//Genesis directory block timestamp in RFC3339 format
	GENESIS_BLK_TIMESTAMP = "2015-09-01T20:00:00+00:00"
	//Genesis directory block hash
	GENESIS_DIR_BLOCK_HASH = "cbd3d09db6defdc25dfc7d57f3479b339a077183cd67022e6d1ef6c041522b40"
)

//---------------------------------------------------------------
// Types of entries (transactions) for Admin Block
// https://github.com/FactomProject/FactomDocs/blob/master/factomDataStructureDetails.md#adminid-bytes
//---------------------------------------------------------------
const (
	TYPE_MINUTE_NUM uint8 = iota
	TYPE_DB_SIGNATURE
	TYPE_REVEAL_MATRYOSHKA
	TYPE_ADD_MATRYOSHKA
	TYPE_ADD_SERVER_COUNT
	TYPE_ADD_FED_SERVER
	TYPE_REMOVE_FED_SERVER
	TYPE_ADD_FED_SERVER_KEY
	TYPE_ADD_BTC_ANCHOR_KEY //8
)

// Chain Values.  Not exactly constants, but nice to have.
// Entry Credit Chain
var EC_CHAINID = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0c}

// Directory Chain
var D_CHAINID = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0d}

// Directory Chain
var ADMIN_CHAINID = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0a}

// Factoid chain
var FACTOID_CHAINID = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0f}

var ZERO_HASH = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

// Structure for reporting properties (used by the web API
//
type Properties struct {
	Protocol_Version int
	Factomd_Version  int
}

func (p *Properties) MarshalJSON() ([]byte, error) {
	type tmp struct {
		Protocol_Version string
		Factomd_Version  string
	}
	t := new(tmp)

	t.Protocol_Version = versionToString(p.Protocol_Version * 1000)
	t.Factomd_Version = versionToString(p.Factomd_Version)

	return json.Marshal(t)
}

// versionToString converts the fixed poit versions to human readable version
// strings.
func versionToString(f int) string {

	v0 := f / 1000000000
	v1 := (f % 1000000000) / 1000000
	v2 := (f % 1000000) / 1000
	v3 := f % 1000

	return fmt.Sprintf("%d.%d.%d.%d", v0, v1, v2, v3)
}
<commit_msg>bump version for Hotfix6<commit_after>// Copyright 2015 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package common

import (
	"encoding/json"
	"fmt"
	"time"
)

const (

	//Entry Credit Blocks (For now, everyone gets the same cap)
	EC_CAP = 5      //Number of ECBlocks we start with.
	AB_CAP = EC_CAP //Administrative Block Cap for AB messages

	//Limits and Sizes
	MAX_ENTRY_SIZE    = uint16(10240) //Maximum size for Entry External IDs and the Data
	HASH_LENGTH       = int(32)       //Length of a Hash
	SIG_LENGTH        = int(64)       //Length of a signature
	MAX_ORPHAN_SIZE   = int(5000)     //Prphan mem pool size
	MAX_TX_POOL_SIZE  = int(50000)    //Transaction mem pool size
	MAX_BLK_POOL_SIZE = int(500000)   //Block mem bool size
	MAX_PLIST_SIZE    = int(150000)   //MY Process List size

	MAX_ENTRY_CREDITS = uint8(10) //Max number of entry credits per entry
	MAX_CHAIN_CREDITS = uint8(20) //Max number of entry credits per chain

	COMMIT_TIME_WINDOW = time.Duration(12) //Time windows for commit chain and commit entry +/- 12 hours

	// maxProtocolVersion is the max protocol version the peer supports.
	//Common constants
	VERSION_0       = byte(0)
	FACTOMD_VERSION = 3006000            //fixed point. resolves to 0.<minor1 release>.<minor2 release>.<minor3 release>
	NETWORK_ID_DB   = uint32(4203931041) //0xFA92E5A1
	NETWORK_ID_EB   = uint32(4203931042) //0xFA92E5A2
	NETWORK_ID_CB   = uint32(4203931043) //0xFA92E5A3

	//For Factom TestNet
	NETWORK_ID_TEST = uint32(0) //0x0

	//Server running mode
	FULL_NODE   = "FULL"
	SERVER_NODE = "SERVER"
	LIGHT_NODE  = "LIGHT"

	//Genesis directory block timestamp in RFC3339 format
	GENESIS_BLK_TIMESTAMP = "2015-09-01T20:00:00+00:00"
	//Genesis directory block hash
	GENESIS_DIR_BLOCK_HASH = "cbd3d09db6defdc25dfc7d57f3479b339a077183cd67022e6d1ef6c041522b40"
)

//---------------------------------------------------------------
// Types of entries (transactions) for Admin Block
// https://github.com/FactomProject/FactomDocs/blob/master/factomDataStructureDetails.md#adminid-bytes
//---------------------------------------------------------------
const (
	TYPE_MINUTE_NUM uint8 = iota
	TYPE_DB_SIGNATURE
	TYPE_REVEAL_MATRYOSHKA
	TYPE_ADD_MATRYOSHKA
	TYPE_ADD_SERVER_COUNT
	TYPE_ADD_FED_SERVER
	TYPE_REMOVE_FED_SERVER
	TYPE_ADD_FED_SERVER_KEY
	TYPE_ADD_BTC_ANCHOR_KEY //8
)

// Chain Values.  Not exactly constants, but nice to have.
// Entry Credit Chain
var EC_CHAINID = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0c}

// Directory Chain
var D_CHAINID = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0d}

// Directory Chain
var ADMIN_CHAINID = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0a}

// Factoid chain
var FACTOID_CHAINID = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0f}

var ZERO_HASH = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

// Structure for reporting properties (used by the web API
//
type Properties struct {
	Protocol_Version int
	Factomd_Version  int
}

func (p *Properties) MarshalJSON() ([]byte, error) {
	type tmp struct {
		Protocol_Version string
		Factomd_Version  string
	}
	t := new(tmp)

	t.Protocol_Version = versionToString(p.Protocol_Version * 1000)
	t.Factomd_Version = versionToString(p.Factomd_Version)

	return json.Marshal(t)
}

// versionToString converts the fixed poit versions to human readable version
// strings.
func versionToString(f int) string {

	v0 := f / 1000000000
	v1 := (f % 1000000000) / 1000000
	v2 := (f % 1000000) / 1000
	v3 := f % 1000

	return fmt.Sprintf("%d.%d.%d.%d", v0, v1, v2, v3)
}
<|endoftext|>


<commit_before>package github

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/crosbymichael/octokat"
)

const (
	pollKey      = "USER POLL"
	pollTemplate = `*USER POLL*

*The best way to get notified of updates is to use the _Subscribe_ button on this page.*

Please don't use "+1" or "I have this too" comments on issues. We automatically
collect those comments to keep the thread short.

The people listed below have upvoted this issue by leaving a +1 comment:
`
)

// LabelIssueComment checks if someone has claimed dibs on this issue
func (g GitHub) LabelIssueComment(issueHook *octokat.IssueHook) error {
	if err := g.maybeClaimIssue(issueHook); err != nil {
		return err
	}

	return g.maybeOpinion(issueHook)
}

func (g GitHub) maybeClaimIssue(issueHook *octokat.IssueHook) error {
	labelmap := map[string]string{
		"#dibs":    "status/claimed",
		"#claimed": "status/claimed",
		"#mine":    "status/claimed",
	}

	repo := nameWithOwner(issueHook.Repo)

	for token, label := range labelmap {
		// if comment matches predefined actions AND author is not bot
		if strings.Contains(strings.ToLower(issueHook.Comment.Body), token) && g.User != issueHook.Sender.Login {
			logrus.Debugf("Adding label %#v to issue %d", label, issueHook.Issue.Number)
			if err := g.addLabel(repo, issueHook.Issue.Number, label); err != nil {
				return err
			}
			logrus.Infof("Added label %#v to issue %d", label, issueHook.Issue.Number)
		}
	}
	return nil
}

func (g GitHub) maybeOpinion(issueHook *octokat.IssueHook) error {
	body := strings.TrimSpace(issueHook.Comment.Body)

	if body == "+1" {
		login := issueHook.Comment.User.Login
		commenters := map[string]int{login: issueHook.Comment.Id}

		options := &octokat.Options{
			QueryParams: map[string]string{"per_page": "100"},
		}

		repo := getRepo(issueHook.Repo)
		issueID := strconv.Itoa(issueHook.Issue.Number)
		comments, err := g.Client().Comments(repo, issueID, options)
		if err != nil {
			return err
		}

		var poll octokat.Comment

		for _, c := range comments {
			if strings.ToLower(c.User.Login) == g.User && strings.Contains(c.Body, pollKey) {
				poll = c
			} else if strings.TrimSpace(c.Body) == "+1" || strings.TrimSpace(c.Body) == ":+1:" {
				commenters[c.User.Login] = c.Id
			}
		}

		if poll.Body != "" {
			if !strings.Contains(poll.Body, login) {
				for k := range commenters {
					poll.Body += fmt.Sprintf("\n@%s", k)
				}
				if _, err := g.Client().PatchComment(repo, strconv.Itoa(poll.Id), poll.Body); err != nil {
					return err
				}
			}
		} else {
			tmpl := pollTemplate
			for k := range commenters {
				tmpl += fmt.Sprintf("\n@%s", k)
			}
			if _, err := g.Client().AddComment(repo, issueID, tmpl); err != nil {
				return err
			}
		}

		for _, v := range commenters {
			if err := g.Client().RemoveComment(repo, v); err != nil {
				return err
			}
		}
	}

	return nil
}

func labelFromVersion(version, suffix string) string {
	switch suffix {
	// Dev suffix is associated with a master build.
	case "dev":
		return "version/master"
	// For a version `X.Y.Z`, add a label of the form `version/X.Y`.
	case strings.HasPrefix(suffix, "cs"):
		fallthrough
	case strings.HasPrefix(suffix, "rc"):
		fallthrough
	case suffix == "":
		return "version/" + version[0:strings.LastIndex(version, ".")]
	// The default for unknown suffix is to consider the version unsupported.
	default:
		return "version/unsupported"
	}
}

func (g GitHub) IssueAddVersionLabel(issueHook *octokat.IssueHook) error {
	serverVersion := regexp.MustCompile(`Server:\s+Version:\s+(\d+\.\d+\.\d+)-?(\S*)`)
	versionSubmatch := serverVersion.FindStringSubmatch(issueHook.Issue.Body)
	if len(versionSubmatch) < 3 {
		return nil
	}

	label := labelFromVersion(versionSubmatch[1], versionSubmatch[2])
	return g.addLabel(nameWithOwner(issueHook.Repo), issueHook.Issue.Number, label)
}
<commit_msg>Fix compilation issue<commit_after>package github

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/crosbymichael/octokat"
)

const (
	pollKey      = "USER POLL"
	pollTemplate = `*USER POLL*

*The best way to get notified of updates is to use the _Subscribe_ button on this page.*

Please don't use "+1" or "I have this too" comments on issues. We automatically
collect those comments to keep the thread short.

The people listed below have upvoted this issue by leaving a +1 comment:
`
)

// LabelIssueComment checks if someone has claimed dibs on this issue
func (g GitHub) LabelIssueComment(issueHook *octokat.IssueHook) error {
	if err := g.maybeClaimIssue(issueHook); err != nil {
		return err
	}

	return g.maybeOpinion(issueHook)
}

func (g GitHub) maybeClaimIssue(issueHook *octokat.IssueHook) error {
	labelmap := map[string]string{
		"#dibs":    "status/claimed",
		"#claimed": "status/claimed",
		"#mine":    "status/claimed",
	}

	repo := nameWithOwner(issueHook.Repo)

	for token, label := range labelmap {
		// if comment matches predefined actions AND author is not bot
		if strings.Contains(strings.ToLower(issueHook.Comment.Body), token) && g.User != issueHook.Sender.Login {
			logrus.Debugf("Adding label %#v to issue %d", label, issueHook.Issue.Number)
			if err := g.addLabel(repo, issueHook.Issue.Number, label); err != nil {
				return err
			}
			logrus.Infof("Added label %#v to issue %d", label, issueHook.Issue.Number)
		}
	}
	return nil
}

func (g GitHub) maybeOpinion(issueHook *octokat.IssueHook) error {
	body := strings.TrimSpace(issueHook.Comment.Body)

	if body == "+1" {
		login := issueHook.Comment.User.Login
		commenters := map[string]int{login: issueHook.Comment.Id}

		options := &octokat.Options{
			QueryParams: map[string]string{"per_page": "100"},
		}

		repo := getRepo(issueHook.Repo)
		issueID := strconv.Itoa(issueHook.Issue.Number)
		comments, err := g.Client().Comments(repo, issueID, options)
		if err != nil {
			return err
		}

		var poll octokat.Comment

		for _, c := range comments {
			if strings.ToLower(c.User.Login) == g.User && strings.Contains(c.Body, pollKey) {
				poll = c
			} else if strings.TrimSpace(c.Body) == "+1" || strings.TrimSpace(c.Body) == ":+1:" {
				commenters[c.User.Login] = c.Id
			}
		}

		if poll.Body != "" {
			if !strings.Contains(poll.Body, login) {
				for k := range commenters {
					poll.Body += fmt.Sprintf("\n@%s", k)
				}
				if _, err := g.Client().PatchComment(repo, strconv.Itoa(poll.Id), poll.Body); err != nil {
					return err
				}
			}
		} else {
			tmpl := pollTemplate
			for k := range commenters {
				tmpl += fmt.Sprintf("\n@%s", k)
			}
			if _, err := g.Client().AddComment(repo, issueID, tmpl); err != nil {
				return err
			}
		}

		for _, v := range commenters {
			if err := g.Client().RemoveComment(repo, v); err != nil {
				return err
			}
		}
	}

	return nil
}

func labelFromVersion(version, suffix string) string {
	switch {
	// Dev suffix is associated with a master build.
	case suffix == "dev":
		return "version/master"
	// For a version `X.Y.Z`, add a label of the form `version/X.Y`.
	case strings.HasPrefix(suffix, "cs"):
		fallthrough
	case strings.HasPrefix(suffix, "rc"):
		fallthrough
	case suffix == "":
		return "version/" + version[0:strings.LastIndex(version, ".")]
	// The default for unknown suffix is to consider the version unsupported.
	default:
		return "version/unsupported"
	}
}

func (g GitHub) IssueAddVersionLabel(issueHook *octokat.IssueHook) error {
	serverVersion := regexp.MustCompile(`Server:\s+Version:\s+(\d+\.\d+\.\d+)-?(\S*)`)
	versionSubmatch := serverVersion.FindStringSubmatch(issueHook.Issue.Body)
	if len(versionSubmatch) < 3 {
		return nil
	}

	label := labelFromVersion(versionSubmatch[1], versionSubmatch[2])
	return g.addLabel(nameWithOwner(issueHook.Repo), issueHook.Issue.Number, label)
}
<|endoftext|>


<commit_before>package v8eval

import (
	"encoding/json"
	"errors"
	"strings"
)

// V8 is a Go interface for JavaScript engine V8
type V8 interface {
	// Eval evaluates the given JavaScript code 'src' and stores the result into 'res'.
	// The result is marshalled/unmarshalled by using JSON.
	// If the result is undefined, 'res' is not changed.
	// If the result cannot be stored into 'res' due to type mismatch, Eval returns the error.
	// If some JavaScript exception happens in runtime, Eval returns the exception as a Go error.
	Eval(src string, res interface{}) error

	// Call calls the JavaScript function specified by 'fun' with the given argument array 'args'
	// and stores the result into 'res'.
	// The arguments and the result are marshalled/unmarshalled by using JSON.
	// If the result is undefined, 'res' is not changed.
	// If the result cannot be stored into 'res' due to type mismatch, Eval returns the error.
	// If some JavaScript exception happens in runtime, Call returns the exception as a Go error.
	Call(fun string, args interface{}, res interface{}) error

	// EnableDebugger starts a debug server associated with the V8 instance.
	// The server will listen on the given TCP/IP port.
	// If failing to start the server, EnableDebugger returns the error.
	EnableDebugger(port int) error

	// DisableDebugger stops the debug server, if running.
	DisableDebugger()
}

type v8 struct {
	xV8 X_GoV8
}

// NewV8 creates a new V8 instance.
func NewV8() V8 {
	v := new(v8)
	v.xV8 = NewX_GoV8()
	return v
}

func (v *v8) decode(str string, val interface{}) error {
	if str == "undefined" {
		return nil
	}

	dec := json.NewDecoder(strings.NewReader(str))
	err := dec.Decode(val)
	if err != nil {
		if strings.HasPrefix(err.Error(), "json: ") {
			return err
		}

		return errors.New(str)
	}

	return nil
}

func (v *v8) Eval(src string, res interface{}) error {
	return v.decode(v.xV8.Eval(src), res)
}

func (v *v8) Call(fun string, args interface{}, res interface{}) error {
	as, err := json.Marshal(args)
	if err != nil {
		return err
	}

	return v.decode(v.xV8.Call(fun, string(as)), res)
}

func (v *v8) EnableDebugger(port int) error {
	if !v.xV8.Enable_debugger(port) {
		return errors.New("failed to start debug server")
	}

	return nil
}

func (v *v8) DisableDebugger() {
	v.xV8.Disable_debugger()
}
<commit_msg>Move a private helper below public methods<commit_after>package v8eval

import (
	"encoding/json"
	"errors"
	"strings"
)

// V8 is a Go interface for JavaScript engine V8
type V8 interface {
	// Eval evaluates the given JavaScript code 'src' and stores the result into 'res'.
	// The result is marshalled/unmarshalled by using JSON.
	// If the result is undefined, 'res' is not changed.
	// If the result cannot be stored into 'res' due to type mismatch, Eval returns the error.
	// If some JavaScript exception happens in runtime, Eval returns the exception as a Go error.
	Eval(src string, res interface{}) error

	// Call calls the JavaScript function specified by 'fun' with the given argument array 'args'
	// and stores the result into 'res'.
	// The arguments and the result are marshalled/unmarshalled by using JSON.
	// If the result is undefined, 'res' is not changed.
	// If the result cannot be stored into 'res' due to type mismatch, Eval returns the error.
	// If some JavaScript exception happens in runtime, Call returns the exception as a Go error.
	Call(fun string, args interface{}, res interface{}) error

	// EnableDebugger starts a debug server associated with the V8 instance.
	// The server will listen on the given TCP/IP port.
	// If failing to start the server, EnableDebugger returns the error.
	EnableDebugger(port int) error

	// DisableDebugger stops the debug server, if running.
	DisableDebugger()
}

type v8 struct {
	xV8 X_GoV8
}

// NewV8 creates a new V8 instance.
func NewV8() V8 {
	v := new(v8)
	v.xV8 = NewX_GoV8()
	return v
}

func (v *v8) Eval(src string, res interface{}) error {
	return v.decode(v.xV8.Eval(src), res)
}

func (v *v8) Call(fun string, args interface{}, res interface{}) error {
	as, err := json.Marshal(args)
	if err != nil {
		return err
	}

	return v.decode(v.xV8.Call(fun, string(as)), res)
}

func (v *v8) decode(str string, val interface{}) error {
	if str == "undefined" {
		return nil
	}

	dec := json.NewDecoder(strings.NewReader(str))
	err := dec.Decode(val)
	if err != nil {
		if strings.HasPrefix(err.Error(), "json: ") {
			return err
		}

		return errors.New(str)
	}

	return nil
}

func (v *v8) EnableDebugger(port int) error {
	if !v.xV8.Enable_debugger(port) {
		return errors.New("failed to start debug server")
	}

	return nil
}

func (v *v8) DisableDebugger() {
	v.xV8.Disable_debugger()
}
<|endoftext|>


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

// This file implements FormatSelections and FormatText.
// FormatText is used to HTML-format Go and non-Go source
// text with line numbers and highlighted sections. It is
// built on top of FormatSelections, a generic formatter
// for "selected" text.

package godoc

import (
	"fmt"
	"go/scanner"
	"go/token"
	"io"
	"regexp"
	"strconv"
	"text/template"
)

// ----------------------------------------------------------------------------
// Implementation of FormatSelections

// A Segment describes a text segment [start, end).
// The zero value of a Segment is a ready-to-use empty segment.
//
type Segment struct {
	start, end int
}

func (seg *Segment) isEmpty() bool { return seg.start >= seg.end }

// A Selection is an "iterator" function returning a text segment.
// Repeated calls to a selection return consecutive, non-overlapping,
// non-empty segments, followed by an infinite sequence of empty
// segments. The first empty segment marks the end of the selection.
//
type Selection func() Segment

// A LinkWriter writes some start or end "tag" to w for the text offset offs.
// It is called by FormatSelections at the start or end of each link segment.
//
type LinkWriter func(w io.Writer, offs int, start bool)

// A SegmentWriter formats a text according to selections and writes it to w.
// The selections parameter is a bit set indicating which selections provided
// to FormatSelections overlap with the text segment: If the n'th bit is set
// in selections, the n'th selection provided to FormatSelections is overlapping
// with the text.
//
type SegmentWriter func(w io.Writer, text []byte, selections int)

// FormatSelections takes a text and writes it to w using link and segment
// writers lw and sw as follows: lw is invoked for consecutive segment starts
// and ends as specified through the links selection, and sw is invoked for
// consecutive segments of text overlapped by the same selections as specified
// by selections. The link writer lw may be nil, in which case the links
// Selection is ignored.
//
func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection, sw SegmentWriter, selections ...Selection) {
	// If we have a link writer, make the links
	// selection the last entry in selections
	if lw != nil {
		selections = append(selections, links)
	}

	// compute the sequence of consecutive segment changes
	changes := newMerger(selections)

	// The i'th bit in bitset indicates that the text
	// at the current offset is covered by selections[i].
	bitset := 0
	lastOffs := 0

	// Text segments are written in a delayed fashion
	// such that consecutive segments belonging to the
	// same selection can be combined (peephole optimization).
	// last describes the last segment which has not yet been written.
	var last struct {
		begin, end int // valid if begin < end
		bitset     int
	}

	// flush writes the last delayed text segment
	flush := func() {
		if last.begin < last.end {
			sw(w, text[last.begin:last.end], last.bitset)
		}
		last.begin = last.end // invalidate last
	}

	// segment runs the segment [lastOffs, end) with the selection
	// indicated by bitset through the segment peephole optimizer.
	segment := func(end int) {
		if lastOffs < end { // ignore empty segments
			if last.end != lastOffs || last.bitset != bitset {
				// the last segment is not adjacent to or
				// differs from the new one
				flush()
				// start a new segment
				last.begin = lastOffs
			}
			last.end = end
			last.bitset = bitset
		}
	}

	for {
		// get the next segment change
		index, offs, start := changes.next()
		if index < 0 || offs > len(text) {
			// no more segment changes or the next change
			// is past the end of the text - we're done
			break
		}
		// determine the kind of segment change
		if lw != nil && index == len(selections)-1 {
			// we have a link segment change (see start of this function):
			// format the previous selection segment, write the
			// link tag and start a new selection segment
			segment(offs)
			flush()
			lastOffs = offs
			lw(w, offs, start)
		} else {
			// we have a selection change:
			// format the previous selection segment, determine
			// the new selection bitset and start a new segment
			segment(offs)
			lastOffs = offs
			mask := 1 << uint(index)
			if start {
				bitset |= mask
			} else {
				bitset &^= mask
			}
		}
	}
	segment(len(text))
	flush()
}

// A merger merges a slice of Selections and produces a sequence of
// consecutive segment change events through repeated next() calls.
//
type merger struct {
	selections []Selection
	segments   []Segment // segments[i] is the next segment of selections[i]
}

const infinity int = 2e9

func newMerger(selections []Selection) *merger {
	segments := make([]Segment, len(selections))
	for i, sel := range selections {
		segments[i] = Segment{infinity, infinity}
		if sel != nil {
			if seg := sel(); !seg.isEmpty() {
				segments[i] = seg
			}
		}
	}
	return &merger{selections, segments}
}

// next returns the next segment change: index specifies the Selection
// to which the segment belongs, offs is the segment start or end offset
// as determined by the start value. If there are no more segment changes,
// next returns an index value < 0.
//
func (m *merger) next() (index, offs int, start bool) {
	// find the next smallest offset where a segment starts or ends
	offs = infinity
	index = -1
	for i, seg := range m.segments {
		switch {
		case seg.start < offs:
			offs = seg.start
			index = i
			start = true
		case seg.end < offs:
			offs = seg.end
			index = i
			start = false
		}
	}
	if index < 0 {
		// no offset found => all selections merged
		return
	}
	// offset found - it's either the start or end offset but
	// either way it is ok to consume the start offset: set it
	// to infinity so it won't be considered in the following
	// next call
	m.segments[index].start = infinity
	if start {
		return
	}
	// end offset found - consume it
	m.segments[index].end = infinity
	// advance to the next segment for that selection
	seg := m.selections[index]()
	if !seg.isEmpty() {
		m.segments[index] = seg
	}
	return
}

// ----------------------------------------------------------------------------
// Implementation of FormatText

// lineSelection returns the line segments for text as a Selection.
func lineSelection(text []byte) Selection {
	i, j := 0, 0
	return func() (seg Segment) {
		// find next newline, if any
		for j < len(text) {
			j++
			if text[j-1] == '\n' {
				break
			}
		}
		if i < j {
			// text[i:j] constitutes a line
			seg = Segment{i, j}
			i = j
		}
		return
	}
}

// tokenSelection returns, as a selection, the sequence of
// consecutive occurrences of token sel in the Go src text.
//
func tokenSelection(src []byte, sel token.Token) Selection {
	var s scanner.Scanner
	fset := token.NewFileSet()
	file := fset.AddFile("", fset.Base(), len(src))
	s.Init(file, src, nil, scanner.ScanComments)
	return func() (seg Segment) {
		for {
			pos, tok, lit := s.Scan()
			if tok == token.EOF {
				break
			}
			offs := file.Offset(pos)
			if tok == sel {
				seg = Segment{offs, offs + len(lit)}
				break
			}
		}
		return
	}
}

// makeSelection is a helper function to make a Selection from a slice of pairs.
// Pairs describing empty segments are ignored.
//
func makeSelection(matches [][]int) Selection {
	i := 0
	return func() Segment {
		for i < len(matches) {
			m := matches[i]
			i++
			if m[0] < m[1] {
				// non-empty segment
				return Segment{m[0], m[1]}
			}
		}
		return Segment{}
	}
}

// regexpSelection computes the Selection for the regular expression expr in text.
func regexpSelection(text []byte, expr string) Selection {
	var matches [][]int
	if rx, err := regexp.Compile(expr); err == nil {
		matches = rx.FindAllIndex(text, -1)
	}
	return makeSelection(matches)
}

var selRx = regexp.MustCompile(`^([0-9]+):([0-9]+)`)

// RangeSelection computes the Selection for a text range described
// by the argument str; the range description must match the selRx
// regular expression.
func RangeSelection(str string) Selection {
	m := selRx.FindStringSubmatch(str)
	if len(m) >= 2 {
		from, _ := strconv.Atoi(m[1])
		to, _ := strconv.Atoi(m[2])
		if from < to {
			return makeSelection([][]int{{from, to}})
		}
	}
	return nil
}

// Span tags for all the possible selection combinations that may
// be generated by FormatText. Selections are indicated by a bitset,
// and the value of the bitset specifies the tag to be used.
//
// bit 0: comments
// bit 1: highlights
// bit 2: selections
//
var startTags = [][]byte{
	/* 000 */ []byte(``),
	/* 001 */ []byte(`<span class="comment">`),
	/* 010 */ []byte(`<span class="highlight">`),
	/* 011 */ []byte(`<span class="highlight-comment">`),
	/* 100 */ []byte(`<span class="selection">`),
	/* 101 */ []byte(`<span class="selection-comment">`),
	/* 110 */ []byte(`<span class="selection-highlight">`),
	/* 111 */ []byte(`<span class="selection-highlight-comment">`),
}

var endTag = []byte(`</span>`)

func selectionTag(w io.Writer, text []byte, selections int) {
	if selections < len(startTags) {
		if tag := startTags[selections]; len(tag) > 0 {
			w.Write(tag)
			template.HTMLEscape(w, text)
			w.Write(endTag)
			return
		}
	}
	template.HTMLEscape(w, text)
}

// FormatText HTML-escapes text and writes it to w.
// Consecutive text segments are wrapped in HTML spans (with tags as
// defined by startTags and endTag) as follows:
//
//	- if line >= 0, line number (ln) spans are inserted before each line,
//	  starting with the value of line
//	- if the text is Go source, comments get the "comment" span class
//	- each occurrence of the regular expression pattern gets the "highlight"
//	  span class
//	- text segments covered by selection get the "selection" span class
//
// Comments, highlights, and selections may overlap arbitrarily; the respective
// HTML span classes are specified in the startTags variable.
//
func FormatText(w io.Writer, text []byte, line int, goSource bool, pattern string, selection Selection) {
	var comments, highlights Selection
	if goSource {
		comments = tokenSelection(text, token.COMMENT)
	}
	if pattern != "" {
		highlights = regexpSelection(text, pattern)
	}
	if line >= 0 || comments != nil || highlights != nil || selection != nil {
		var lineTag LinkWriter
		if line >= 0 {
			lineTag = func(w io.Writer, _ int, start bool) {
				if start {
					fmt.Fprintf(w, "%6d\t", line, line)
					line++
				}
			}
		}
		FormatSelections(w, text, lineTag, lineSelection(text), selectionTag, comments, highlights, selection)
	} else {
		template.HTMLEscape(w, text)
	}
}
<commit_msg>go.tools/godoc: Fix jump-to-line in source view.<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file implements FormatSelections and FormatText.
// FormatText is used to HTML-format Go and non-Go source
// text with line numbers and highlighted sections. It is
// built on top of FormatSelections, a generic formatter
// for "selected" text.

package godoc

import (
	"fmt"
	"go/scanner"
	"go/token"
	"io"
	"regexp"
	"strconv"
	"text/template"
)

// ----------------------------------------------------------------------------
// Implementation of FormatSelections

// A Segment describes a text segment [start, end).
// The zero value of a Segment is a ready-to-use empty segment.
//
type Segment struct {
	start, end int
}

func (seg *Segment) isEmpty() bool { return seg.start >= seg.end }

// A Selection is an "iterator" function returning a text segment.
// Repeated calls to a selection return consecutive, non-overlapping,
// non-empty segments, followed by an infinite sequence of empty
// segments. The first empty segment marks the end of the selection.
//
type Selection func() Segment

// A LinkWriter writes some start or end "tag" to w for the text offset offs.
// It is called by FormatSelections at the start or end of each link segment.
//
type LinkWriter func(w io.Writer, offs int, start bool)

// A SegmentWriter formats a text according to selections and writes it to w.
// The selections parameter is a bit set indicating which selections provided
// to FormatSelections overlap with the text segment: If the n'th bit is set
// in selections, the n'th selection provided to FormatSelections is overlapping
// with the text.
//
type SegmentWriter func(w io.Writer, text []byte, selections int)

// FormatSelections takes a text and writes it to w using link and segment
// writers lw and sw as follows: lw is invoked for consecutive segment starts
// and ends as specified through the links selection, and sw is invoked for
// consecutive segments of text overlapped by the same selections as specified
// by selections. The link writer lw may be nil, in which case the links
// Selection is ignored.
//
func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection, sw SegmentWriter, selections ...Selection) {
	// If we have a link writer, make the links
	// selection the last entry in selections
	if lw != nil {
		selections = append(selections, links)
	}

	// compute the sequence of consecutive segment changes
	changes := newMerger(selections)

	// The i'th bit in bitset indicates that the text
	// at the current offset is covered by selections[i].
	bitset := 0
	lastOffs := 0

	// Text segments are written in a delayed fashion
	// such that consecutive segments belonging to the
	// same selection can be combined (peephole optimization).
	// last describes the last segment which has not yet been written.
	var last struct {
		begin, end int // valid if begin < end
		bitset     int
	}

	// flush writes the last delayed text segment
	flush := func() {
		if last.begin < last.end {
			sw(w, text[last.begin:last.end], last.bitset)
		}
		last.begin = last.end // invalidate last
	}

	// segment runs the segment [lastOffs, end) with the selection
	// indicated by bitset through the segment peephole optimizer.
	segment := func(end int) {
		if lastOffs < end { // ignore empty segments
			if last.end != lastOffs || last.bitset != bitset {
				// the last segment is not adjacent to or
				// differs from the new one
				flush()
				// start a new segment
				last.begin = lastOffs
			}
			last.end = end
			last.bitset = bitset
		}
	}

	for {
		// get the next segment change
		index, offs, start := changes.next()
		if index < 0 || offs > len(text) {
			// no more segment changes or the next change
			// is past the end of the text - we're done
			break
		}
		// determine the kind of segment change
		if lw != nil && index == len(selections)-1 {
			// we have a link segment change (see start of this function):
			// format the previous selection segment, write the
			// link tag and start a new selection segment
			segment(offs)
			flush()
			lastOffs = offs
			lw(w, offs, start)
		} else {
			// we have a selection change:
			// format the previous selection segment, determine
			// the new selection bitset and start a new segment
			segment(offs)
			lastOffs = offs
			mask := 1 << uint(index)
			if start {
				bitset |= mask
			} else {
				bitset &^= mask
			}
		}
	}
	segment(len(text))
	flush()
}

// A merger merges a slice of Selections and produces a sequence of
// consecutive segment change events through repeated next() calls.
//
type merger struct {
	selections []Selection
	segments   []Segment // segments[i] is the next segment of selections[i]
}

const infinity int = 2e9

func newMerger(selections []Selection) *merger {
	segments := make([]Segment, len(selections))
	for i, sel := range selections {
		segments[i] = Segment{infinity, infinity}
		if sel != nil {
			if seg := sel(); !seg.isEmpty() {
				segments[i] = seg
			}
		}
	}
	return &merger{selections, segments}
}

// next returns the next segment change: index specifies the Selection
// to which the segment belongs, offs is the segment start or end offset
// as determined by the start value. If there are no more segment changes,
// next returns an index value < 0.
//
func (m *merger) next() (index, offs int, start bool) {
	// find the next smallest offset where a segment starts or ends
	offs = infinity
	index = -1
	for i, seg := range m.segments {
		switch {
		case seg.start < offs:
			offs = seg.start
			index = i
			start = true
		case seg.end < offs:
			offs = seg.end
			index = i
			start = false
		}
	}
	if index < 0 {
		// no offset found => all selections merged
		return
	}
	// offset found - it's either the start or end offset but
	// either way it is ok to consume the start offset: set it
	// to infinity so it won't be considered in the following
	// next call
	m.segments[index].start = infinity
	if start {
		return
	}
	// end offset found - consume it
	m.segments[index].end = infinity
	// advance to the next segment for that selection
	seg := m.selections[index]()
	if !seg.isEmpty() {
		m.segments[index] = seg
	}
	return
}

// ----------------------------------------------------------------------------
// Implementation of FormatText

// lineSelection returns the line segments for text as a Selection.
func lineSelection(text []byte) Selection {
	i, j := 0, 0
	return func() (seg Segment) {
		// find next newline, if any
		for j < len(text) {
			j++
			if text[j-1] == '\n' {
				break
			}
		}
		if i < j {
			// text[i:j] constitutes a line
			seg = Segment{i, j}
			i = j
		}
		return
	}
}

// tokenSelection returns, as a selection, the sequence of
// consecutive occurrences of token sel in the Go src text.
//
func tokenSelection(src []byte, sel token.Token) Selection {
	var s scanner.Scanner
	fset := token.NewFileSet()
	file := fset.AddFile("", fset.Base(), len(src))
	s.Init(file, src, nil, scanner.ScanComments)
	return func() (seg Segment) {
		for {
			pos, tok, lit := s.Scan()
			if tok == token.EOF {
				break
			}
			offs := file.Offset(pos)
			if tok == sel {
				seg = Segment{offs, offs + len(lit)}
				break
			}
		}
		return
	}
}

// makeSelection is a helper function to make a Selection from a slice of pairs.
// Pairs describing empty segments are ignored.
//
func makeSelection(matches [][]int) Selection {
	i := 0
	return func() Segment {
		for i < len(matches) {
			m := matches[i]
			i++
			if m[0] < m[1] {
				// non-empty segment
				return Segment{m[0], m[1]}
			}
		}
		return Segment{}
	}
}

// regexpSelection computes the Selection for the regular expression expr in text.
func regexpSelection(text []byte, expr string) Selection {
	var matches [][]int
	if rx, err := regexp.Compile(expr); err == nil {
		matches = rx.FindAllIndex(text, -1)
	}
	return makeSelection(matches)
}

var selRx = regexp.MustCompile(`^([0-9]+):([0-9]+)`)

// RangeSelection computes the Selection for a text range described
// by the argument str; the range description must match the selRx
// regular expression.
func RangeSelection(str string) Selection {
	m := selRx.FindStringSubmatch(str)
	if len(m) >= 2 {
		from, _ := strconv.Atoi(m[1])
		to, _ := strconv.Atoi(m[2])
		if from < to {
			return makeSelection([][]int{{from, to}})
		}
	}
	return nil
}

// Span tags for all the possible selection combinations that may
// be generated by FormatText. Selections are indicated by a bitset,
// and the value of the bitset specifies the tag to be used.
//
// bit 0: comments
// bit 1: highlights
// bit 2: selections
//
var startTags = [][]byte{
	/* 000 */ []byte(``),
	/* 001 */ []byte(`<span class="comment">`),
	/* 010 */ []byte(`<span class="highlight">`),
	/* 011 */ []byte(`<span class="highlight-comment">`),
	/* 100 */ []byte(`<span class="selection">`),
	/* 101 */ []byte(`<span class="selection-comment">`),
	/* 110 */ []byte(`<span class="selection-highlight">`),
	/* 111 */ []byte(`<span class="selection-highlight-comment">`),
}

var endTag = []byte(`</span>`)

func selectionTag(w io.Writer, text []byte, selections int) {
	if selections < len(startTags) {
		if tag := startTags[selections]; len(tag) > 0 {
			w.Write(tag)
			template.HTMLEscape(w, text)
			w.Write(endTag)
			return
		}
	}
	template.HTMLEscape(w, text)
}

// FormatText HTML-escapes text and writes it to w.
// Consecutive text segments are wrapped in HTML spans (with tags as
// defined by startTags and endTag) as follows:
//
//	- if line >= 0, line number (ln) spans are inserted before each line,
//	  starting with the value of line
//	- if the text is Go source, comments get the "comment" span class
//	- each occurrence of the regular expression pattern gets the "highlight"
//	  span class
//	- text segments covered by selection get the "selection" span class
//
// Comments, highlights, and selections may overlap arbitrarily; the respective
// HTML span classes are specified in the startTags variable.
//
func FormatText(w io.Writer, text []byte, line int, goSource bool, pattern string, selection Selection) {
	var comments, highlights Selection
	if goSource {
		comments = tokenSelection(text, token.COMMENT)
	}
	if pattern != "" {
		highlights = regexpSelection(text, pattern)
	}
	if line >= 0 || comments != nil || highlights != nil || selection != nil {
		var lineTag LinkWriter
		if line >= 0 {
			lineTag = func(w io.Writer, _ int, start bool) {
				if start {
					fmt.Fprintf(w, "%6d\t", line, line)
					line++
				}
			}
		}
		FormatSelections(w, text, lineTag, lineSelection(text), selectionTag, comments, highlights, selection)
	} else {
		template.HTMLEscape(w, text)
	}
}
<|endoftext|>


<commit_before>// Package gohinetradio is to get hichannel radio path and with token to play without flash.
package gohinetradio

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"runtime"
	"sort"
	"strconv"
	"sync"
	"text/tabwriter"
)

var nCPU = runtime.NumCPU()

// Init.
func init() {
	runtime.GOMAXPROCS(nCPU)
}

// Base URL.
const (
	PLAYURL  string = "http://hichannel.hinet.net/radio/play.do?id=%s"
	LISTURL  string = "http://hichannel.hinet.net/radio/channelList.do?radioType=&freqType=&freq=&area=&pN=%d"
	LISTPAGE int    = 4
)

// RadioData is the json of `http://hichannel.hinet.net/radio/play.do?id=232`
type RadioData struct {
	ChannelTitle   string `json:"channel_title"`
	PlayRadio      string `json:"playRadio"`
	ProgramName    string `json:"programName"`
	ChannelCollect bool   `json:"channel_collect"`
}

// GetURL is getting radio channel url with token.
func GetURL(No string) (RadioData, error) {
	resp, _ := http.Get(fmt.Sprintf(PLAYURL, No))
	defer resp.Body.Close()
	var r RadioData
	var err error
	data, _ := ioutil.ReadAll(resp.Body)
	jsonData := json.NewDecoder(bytes.NewReader(data))
	jsonData.Decode(&r)
	if len(r.PlayRadio) == 0 {
		err = errors.New("No channel data.")
	}
	return r, err
}

// RadioListData is the json of `http://hichannel.hinet.net/radio/channelList.do?radioType=&freqType=&freq=&area=&pN=1`
type RadioListData struct {
	PageNo   int              `json:"pageNo"`
	PageSize int              `json:"pageSize"`
	List     []RadioListDatas `json:"list"`
}

//RadioListDatas is RadioListData.List type.
type RadioListDatas struct {
	ChannelImage string `json:"channel_image"`
	ChannelTitle string `json:"channel_title"`
	RadioType    string `json:"radio_type"`
	IsChannel    bool   `json:"isChannel"`
	ProgramName  string `json:"program_name"`
	ChannelID    string `json:"channel_id"`
}

func getRadioPageList(page int) RadioListData {
	resp, _ := http.Get(fmt.Sprintf(LISTURL, page))
	defer resp.Body.Close()
	var r RadioListData
	data, _ := ioutil.ReadAll(resp.Body)
	jsonData := json.NewDecoder(bytes.NewReader(data))
	jsonData.Decode(&r)
	return r
}

// GetRadioList is getting all channel list.
func GetRadioList() []RadioListDatas {
	queue := make(chan RadioListData, nCPU)
	var wg sync.WaitGroup
	wg.Add(LISTPAGE)
	for i := 1; i <= LISTPAGE; i++ {
		go func(i int) {
			runtime.Gosched()
			queue <- getRadioPageList(i)
		}(i)
	}
	var r []RadioListDatas
	go func() {
		for v := range queue {
			for _, data := range v.List {
				r = append(r, data)
			}
			wg.Done()
		}
	}()
	wg.Wait()
	return r
}

type byChannel []RadioListDatas

func (c byChannel) Len() int      { return len(c) }
func (c byChannel) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c byChannel) Less(i, j int) bool {
	a, _ := strconv.Atoi(c[i].ChannelID)
	b, _ := strconv.Atoi(c[j].ChannelID)
	return a < b
}

// GenList is to output table list.
func GenList() {
	w := new(tabwriter.Writer)
	w.Init(os.Stdout, 0, 8, 0, '\t', 0)
	var output string
	var no int
	radioList := GetRadioList()
	sort.Sort(byChannel(radioList))
	for _, data := range radioList {
		if data.IsChannel {
			output += fmt.Sprintf("%d. [%v] %s\t", no+1, data.ChannelID, data.ChannelTitle)
			if (no+1)%3 == 0 {
				fmt.Fprintln(w, output)
				output = ""
			}
			no++
		}
	}
	fmt.Fprintln(w, output)
	w.Flush()
}

// PrintChannel is my fav channel XD.
func PrintChannel() {
	fmt.Println("[207] 中廣新聞網")
	fmt.Println("[205] 中廣流行網 i like")
	fmt.Println("[206] 中廣音樂網i radio")
	fmt.Println("[232] 飛碟電台")
	fmt.Println("[222] HitFm聯播網 Taipei 北部")
	fmt.Println("[156] KISS RADIO 大眾廣播電台")
	fmt.Println("[308] KISS RADIO 網路音樂台")
	fmt.Println("[187] NEWS98新聞網")
	fmt.Println("[370] POP Radio 台北流行廣播電台")
}
<commit_msg>Add close chan.<commit_after>// Package gohinetradio is to get hichannel radio path and with token to play without flash.
package gohinetradio

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"runtime"
	"sort"
	"strconv"
	"sync"
	"text/tabwriter"
)

var nCPU = runtime.NumCPU()

// Init.
func init() {
	runtime.GOMAXPROCS(nCPU)
}

// Base URL.
const (
	PLAYURL  string = "http://hichannel.hinet.net/radio/play.do?id=%s"
	LISTURL  string = "http://hichannel.hinet.net/radio/channelList.do?radioType=&freqType=&freq=&area=&pN=%d"
	LISTPAGE int    = 4
)

// RadioData is the json of `http://hichannel.hinet.net/radio/play.do?id=232`
type RadioData struct {
	ChannelTitle   string `json:"channel_title"`
	PlayRadio      string `json:"playRadio"`
	ProgramName    string `json:"programName"`
	ChannelCollect bool   `json:"channel_collect"`
}

// GetURL is getting radio channel url with token.
func GetURL(No string) (RadioData, error) {
	resp, _ := http.Get(fmt.Sprintf(PLAYURL, No))
	defer resp.Body.Close()
	var r RadioData
	var err error
	data, _ := ioutil.ReadAll(resp.Body)
	jsonData := json.NewDecoder(bytes.NewReader(data))
	jsonData.Decode(&r)
	if len(r.PlayRadio) == 0 {
		err = errors.New("No channel data.")
	}
	return r, err
}

// RadioListData is the json of `http://hichannel.hinet.net/radio/channelList.do?radioType=&freqType=&freq=&area=&pN=1`
type RadioListData struct {
	PageNo   int              `json:"pageNo"`
	PageSize int              `json:"pageSize"`
	List     []RadioListDatas `json:"list"`
}

//RadioListDatas is RadioListData.List type.
type RadioListDatas struct {
	ChannelImage string `json:"channel_image"`
	ChannelTitle string `json:"channel_title"`
	RadioType    string `json:"radio_type"`
	IsChannel    bool   `json:"isChannel"`
	ProgramName  string `json:"program_name"`
	ChannelID    string `json:"channel_id"`
}

func getRadioPageList(page int) RadioListData {
	resp, _ := http.Get(fmt.Sprintf(LISTURL, page))
	defer resp.Body.Close()
	var r RadioListData
	data, _ := ioutil.ReadAll(resp.Body)
	jsonData := json.NewDecoder(bytes.NewReader(data))
	jsonData.Decode(&r)
	return r
}

// GetRadioList is getting all channel list.
func GetRadioList() []RadioListDatas {
	queue := make(chan RadioListData, nCPU)
	var wg sync.WaitGroup
	wg.Add(LISTPAGE)
	for i := 1; i <= LISTPAGE; i++ {
		go func(i int) {
			runtime.Gosched()
			queue <- getRadioPageList(i)
		}(i)
	}
	var r []RadioListDatas
	go func() {
		for v := range queue {
			for _, data := range v.List {
				r = append(r, data)
			}
			wg.Done()
		}
	}()
	wg.Wait()
	close(queue)
	return r
}

type byChannel []RadioListDatas

func (c byChannel) Len() int      { return len(c) }
func (c byChannel) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c byChannel) Less(i, j int) bool {
	a, _ := strconv.Atoi(c[i].ChannelID)
	b, _ := strconv.Atoi(c[j].ChannelID)
	return a < b
}

// GenList is to output table list.
func GenList() {
	w := new(tabwriter.Writer)
	w.Init(os.Stdout, 0, 8, 0, '\t', 0)
	var output string
	var no int
	radioList := GetRadioList()
	sort.Sort(byChannel(radioList))
	for _, data := range radioList {
		if data.IsChannel {
			output += fmt.Sprintf("%d. [%v] %s\t", no+1, data.ChannelID, data.ChannelTitle)
			if (no+1)%3 == 0 {
				fmt.Fprintln(w, output)
				output = ""
			}
			no++
		}
	}
	fmt.Fprintln(w, output)
	w.Flush()
}

// PrintChannel is my fav channel XD.
func PrintChannel() {
	fmt.Println("[207] 中廣新聞網")
	fmt.Println("[205] 中廣流行網 i like")
	fmt.Println("[206] 中廣音樂網i radio")
	fmt.Println("[232] 飛碟電台")
	fmt.Println("[222] HitFm聯播網 Taipei 北部")
	fmt.Println("[156] KISS RADIO 大眾廣播電台")
	fmt.Println("[308] KISS RADIO 網路音樂台")
	fmt.Println("[187] NEWS98新聞網")
	fmt.Println("[370] POP Radio 台北流行廣播電台")
}
<|endoftext|>


<commit_before>/*Package gorandpacket is a go library for randomly generating ethernet packets
It's main purpose is for testing networking software/hardware
*/
package gorandpacket

import (
	"code.google.com/p/gopacket/layers"
	"math/rand"
	"net"
	"time"
	"code.google.com/p/gopacket"
)

//Struct RandPacket holds the gorandpacket object
type RandPacketT struct {
	MACLen int
	Seed   int64
	Rand   *rand.Rand
}
//NewGorandPacket Factory method for gorandpacket
func NewGorandPacket() RandPacketT {

	rp := RandPacketT{}
	rp.MACLen = 6
	rp.Seed = int64(time.Now().Nanosecond())
	rp.Rand = rand.New(rand.NewSource(rp.Seed))
	return rp

}

//RandIPv4Addr generates a random IPv4 address
func (r *RandPacketT) RandIPv4Addr() net.IP {
	var myIP net.IP
	myIP = make(net.IP, net.IPv4len)

	for i := 0; i < len(myIP); i++ {
		myIP[i] = r.RandByte()
	}
	return myIP
}

//RandIPv6Addr Generate a random IPv6 address
func (r *RandPacketT) RandIPv6Addr() net.IP {
	var myIP net.IP
	myIP = make(net.IP, net.IPv6len)

	for i := 0; i < len(myIP); i++ {
		myIP[i] = r.RandByte()
	}
	return myIP
}

//RandMACAddr Generate a random MAC address
func (r *RandPacketT) RandMACAddr() net.HardwareAddr {

	var myMAC net.HardwareAddr
	myMAC = make(net.HardwareAddr, r.MACLen)
	for i := 0; i < r.MACLen; i++ {
		myMAC[i] = r.RandByte()
	}
	return myMAC

}
//SetSeed Set a seed from an external source
func (r *RandPacketT) SetSeed(s int64) {

	r.Seed = s

}

//RandInt16 generates random uint16
func (r *RandPacketT) RandInt16() uint16 {
	num := r.Rand.Intn(0xFFFF)
	num16 := uint16(num)
	return num16
}

//RandInt32 generates random uint32
func (r *RandPacketT) RandInt32() uint32 {

	num := r.Rand.Intn(0xFFFFFFFF)
	num32 := uint32(num)
	return num32
}

//RandByte Generate a random byte
func (r *RandPacketT) RandByte() byte {

	num := r.Rand.Intn(255)
	mByte := uint8(num)

	return mByte
}
//RandInt generates a random int
func (r *RandPacketT) RandInt(n int) int {
	return r.Rand.Intn(n)
}

//RandPayload generates a Random Payload
func (r *RandPacketT) RandPayload() []byte {

	pSize := r.RandInt(1000)

	var myPayload []byte
	myPayload = make([]byte, pSize)

	for i := 5; i < int(pSize); i++ {
		myPayload[i] = r.RandByte()
		println(myPayload[i])

	}
	return myPayload

}
//RandIPv4Layer generates a random IPv4 layer
func (r *RandPacketT) RandIPv4Layer() *layers.IPv4 {

	ipv4 := layers.IPv4{
		Version:    uint8(4),
		IHL:        uint8(5),
		TOS:        uint8(0x1),
		Length:     uint16(40),
		Id:         uint16(0xFFFF),
		Flags:      layers.IPv4Flag(0),
		FragOffset: uint16(0),
		TTL:        uint8(0x1),
		Protocol:   layers.IPProtocolTCP,
		Checksum:   uint16(0),
		SrcIP:      r.RandIPv4Addr(),
		DstIP:      r.RandIPv4Addr(),
	}
	return &ipv4
}

//RandIPv4TCPLayer generates a random TCP layer
func (r *RandPacketT) RandIPv4TCPLayer() *layers.TCP {

	ipv4Tcp := layers.TCP{
		SrcPort:    layers.TCPPort(r.RandInt16()), //uint16
		DstPort:    layers.TCPPort(r.RandInt16()), //uint16
		Seq:        0xFFFFFFFF,                    //uint32
		Ack:        0x2,                           //uint32
		DataOffset: uint8(5),                      //must be 5                             //uint8, higer 4 bits are 0.
		FIN:        false,
		SYN:        false,
		RST:        false,
		PSH:        false,
		ACK:        false,
		URG:        false,
		ECE:        false,
		CWR:        false,
		NS:         false,
		Window:     0xff,
		Checksum:   uint16(0),
		Urgent:     0xFF, //uint16
	}
	return &ipv4Tcp

}
//RandEthernetLayer generates a random Ethernet layer
func (r *RandPacketT) RandEthernetLayer() *layers.Ethernet {
	eth := layers.Ethernet{}
	eth.EthernetType = layers.EthernetTypeIPv4
	eth.SrcMAC = r.RandMACAddr()
	eth.DstMAC = r.RandMACAddr()
	return &eth

}
/*RandEthernetPacket generates a random ethernet packet.
For now it only generates IPv4/TCP packets
TODO:  Generate more types/protocols
*/
func (r *RandPacketT) RandEthernetPacket() gopacket.SerializeBuffer {

	buf := gopacket.NewSerializeBuffer()

	// See gopacket SerializeOptions for more details.
	opts := gopacket.SerializeOptions{}

	// Generate a random ethernet layer
	eth := r.RandEthernetLayer()

	// Generate a random IPV4 Layer
	//TODO: randomize ip version
	ipv4 :=  r.RandIPv4Layer()

	// Generate a random TCP layer
	ipv4Tcp := r.RandIPv4TCPLayer()

	//Serilize layers.
	err := gopacket.SerializeLayers(buf, opts,eth,ipv4,ipv4Tcp,
		gopacket.Payload(r.RandPayload()),
	)

	if err != nil {
		panic(err)
	}

	return buf

}
<commit_msg>Added support to generata a random packet including IPv4, TCP, UDP<commit_after>/*Package gorandpacket is a go library for randomly generating ethernet packets
It's main purpose is for testing networking software/hardware
*/
package gorandpacket

import (
	"code.google.com/p/gopacket"
	"code.google.com/p/gopacket/layers"
	//"fmt"
	"math/rand"
	"net"
	"time"
)

//Struct RandPacket holds the gorandpacket object
type RandPacketT struct {
	MACLen int
	Seed   int64
	Rand   *rand.Rand
}

//NewGorandPacket Factory method for gorandpacket
func NewGorandPacket() RandPacketT {

	rp := RandPacketT{}
	rp.MACLen = 6
	rp.Seed = int64(time.Now().Nanosecond())
	rp.Rand = rand.New(rand.NewSource(rp.Seed))
	return rp

}

//RandIPv4Addr generates a random IPv4 address
func (r *RandPacketT) RandIPv4Addr() net.IP {
	var myIP net.IP
	myIP = make(net.IP, net.IPv4len)

	for i := 0; i < len(myIP); i++ {
		myIP[i] = r.RandByte()
	}
	return myIP
}

//RandIPv6Addr Generate a random IPv6 address
func (r *RandPacketT) RandIPv6Addr() net.IP {
	var myIP net.IP
	myIP = make(net.IP, net.IPv6len)

	for i := 0; i < len(myIP); i++ {
		myIP[i] = r.RandByte()
	}
	return myIP
}

//RandMACAddr Generate a random MAC address
func (r *RandPacketT) RandMACAddr() net.HardwareAddr {

	var myMAC net.HardwareAddr
	myMAC = make(net.HardwareAddr, r.MACLen)
	for i := 0; i < r.MACLen; i++ {
		myMAC[i] = r.RandByte()
	}
	return myMAC

}

//SetSeed Set a seed from an external source
func (r *RandPacketT) SetSeed(s int64) {

	r.Seed = s

}

//RandInt16 generates random uint16
func (r *RandPacketT) RandInt16() uint16 {
	num := r.Rand.Intn(0xFFFF)
	num16 := uint16(num)
	return num16
}

//RandInt32 generates random uint32
func (r *RandPacketT) RandInt32() uint32 {

	num := r.Rand.Intn(0xFFFFFFFF)
	num32 := uint32(num)
	return num32
}

//RandByte Generate a random byte
func (r *RandPacketT) RandByte() byte {

	num := r.Rand.Intn(255)
	mByte := uint8(num)

	return mByte
}

//RandInt generates a random int
func (r *RandPacketT) RandInt(n int) int {
	return r.Rand.Intn(n)
}

//RandPayload generates a Random Payload
func (r *RandPacketT) RandPayload() []byte {

	pSize := r.RandInt(1000)

	var myPayload []byte
	myPayload = make([]byte, pSize)

	for i := 5; i < int(pSize); i++ {
		myPayload[i] = r.RandByte()
		//println(myPayload[i])

	}
	return myPayload

}

//RandIPv4Layer generates a random IPv4 layer
func (r *RandPacketT) RandIPv4Layer() *layers.IPv4 {
	const (
		l3tcp = iota // c0 == 0
		l3udp = iota // c1 == 1
	)
	var l3protocol uint8
	//Randomly choose the l3 protocol to be used
	switch r.Rand.Intn(2) {
	case l3tcp:
		l3protocol = uint8(layers.IPProtocolTCP)
	case l3udp:
		l3protocol = uint8(layers.IPProtocolUDP)
	}

	ipv4 := layers.IPv4{
		Version:    uint8(4),
		IHL:        uint8(5),
		TOS:        uint8(0x1),
		Length:     uint16(40),
		Id:         uint16(r.RandInt16()),
		Flags:      layers.IPv4Flag(0),
		FragOffset: uint16(0),
		TTL:        uint8(0x1),
		Protocol:   layers.IPProtocol(l3protocol),
		Checksum:   uint16(0),
		SrcIP:      r.RandIPv4Addr(),
		DstIP:      r.RandIPv4Addr(),
	}
	return &ipv4
}

//RandIPUDP generates a random UDP layer
func (r *RandPacketT) RandIPUDPLayer() *layers.UDP {

	udp := layers.UDP{
		SrcPort:  layers.UDPPort(r.RandInt16()),
		DstPort:  layers.UDPPort(r.RandInt16()),
		Length:   8,
		Checksum: 0,
	}
	return &udp
}

//RandIPv4TCPLayer generates a random TCP layer
func (r *RandPacketT) RandIPTCPLayer() *layers.TCP {

	ipv4Tcp := layers.TCP{
		SrcPort:    layers.TCPPort(r.RandInt16()), //uint16
		DstPort:    layers.TCPPort(r.RandInt16()), //uint16
		Seq:        0xFFFFFFFF,                    //uint32
		Ack:        0x2,                           //uint32
		DataOffset: uint8(5),                      //must be 5                             //uint8, higer 4 bits are 0.
		FIN:        false,
		SYN:        false,
		RST:        false,
		PSH:        false,
		ACK:        false,
		URG:        false,
		ECE:        false,
		CWR:        false,
		NS:         false,
		Window:     0xff,
		Checksum:   uint16(0),
		Urgent:     0xFF, //uint16
	}
	return &ipv4Tcp

}

//RandEthernetLayer generates a random Ethernet layer
func (r *RandPacketT) RandEthernetLayer() *layers.Ethernet {
	eth := layers.Ethernet{}
	eth.EthernetType = layers.EthernetTypeIPv4
	eth.SrcMAC = r.RandMACAddr()
	eth.DstMAC = r.RandMACAddr()
	return &eth

}

//RandL3Layer generates a random L3 layer:  currently supports TCP & UDP only.
func (r *RandPacketT) RandL3Layer(l3type layers.IPProtocol) gopacket.SerializableLayer {

	var l3 gopacket.SerializableLayer

	/*********************
	Create a Random L3 layer
	**********************/
	switch l3type {
	case layers.IPProtocolTCP:
		// Generate a random TCP layer
		l3 = r.RandIPTCPLayer()
	case layers.IPProtocolUDP:
		//Generate a random UDP layer
		l3 = r.RandIPUDPLayer()
	default:
		panic("Bad l3 packet type")
	}
	return l3
}

/*RandEthernetPacket generates a random ethernet packet.
For now it only generates IPv4/TCP packets
TODO:  Generate more types/protocols
*/
func (r *RandPacketT) RandEthernetPacket() gopacket.SerializeBuffer {

	buf := gopacket.NewSerializeBuffer()

	// See gopacket SerializeOptions for more details.
	opts := gopacket.SerializeOptions{}

	// Generate a random ethernet layer
	eth := r.RandEthernetLayer()

	// Generate a random IPV4 Layer
	//TODO: randomize ip version
	l3 := r.RandIPv4Layer()

	//Generate a random IP L4 layer.
	l4 := r.RandL3Layer(l3.Protocol)

	/*****************************
	Check IP layer size
	******************************/
	l3Buf := gopacket.NewSerializeBuffer()
	l4Buf := gopacket.NewSerializeBuffer()
	err := l3.SerializeTo(l3Buf, opts)
	err = l4.SerializeTo(l4Buf, opts)

	l3.Length = uint16(len(l3Buf.Bytes())) + uint16(len(l4Buf.Bytes()))
	/*****************************
	Generate the final ethernet frame
	by serializing all generated layers
	*****************************/
	err = gopacket.SerializeLayers(buf, opts, eth, l3, l4)
	//fmt.Println(buf)
	if err != nil {
		panic(err)
	}
	return buf
}
<|endoftext|>


<commit_before>// Copyright © 2009-2010 Esko Luontola <www.orfjackal.net>
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0

package examples

import (
	"container/list"
	. "gospec"
	"os"
)

func ExpectationExamplesSpec(c Context) {

	c.Specify("Objects can be compared for equality", func() {
		c.Expect(1, Equals, 1)
		c.Expect("string", Equals, "string")

		// There are some shorthands for commonly used comparisons:
		c.Expect(true, IsTrue)
		c.Expect(false, IsFalse)
		c.Expect(nil, IsNil)
		var typedNilPointerInsideInterfaceValue *os.File
		c.Expect(typedNilPointerInsideInterfaceValue, IsNil)

		// Comparing pointer equality is also possible:
		p1 := &Point2{1, 2}
		p2 := p1
		p3 := &Point2{1, 2}
		c.Expect(p2, IsSame, p1)
		c.Expect(p3, Not(IsSame), p1)

		// Comparing floats for equality is not recommended, because
		// floats are rarely exactly equal. So don't write like this:
		c.Expect(3.141, Equals, 3.141)
		// But instead compare using a delta and write like this:
		c.Expect(3.141, IsWithin(0.001), 3.1415926535)

		// Objects with an "Equals(interface{}) bool" method can be
		// compared for equality. See "point.go" for details of how
		// the Equals(interface{}) method should be written. Special
		// care is needed if the objects are used both as values and
		// as pointers.
		a1 := Point2{1, 2}
		a2 := Point2{1, 2}
		c.Expect(a1, Equals, a2)

		b1 := &Point3{1, 2, 3}
		b2 := &Point3{1, 2, 3}
		c.Expect(b1, Equals, b2)
	})

	c.Specify("All expectations can be negated", func() {
		c.Expect(1, Not(Equals), 2)
		c.Expect("apples", Not(Equals), "oranges")
		c.Expect(new(int), Not(IsNil))
	})

	c.Specify("Boolean expressions can be stated about an object", func() {
		s := "some string"
		c.Expect(s, Satisfies, len(s) >= 10 && len(s) <= 20)
		c.Expect(s, Not(Satisfies), len(s) == 0)
	})

	c.Specify("Custom matchers can be defined for commonly used expressions", func() {
		s1 := "first string"
		s2 := "other string"
		c.Expect(s1, HasSameLengthAs, s2)
	})

	c.Specify("Arrays, slices, iterables and channels can be tested for containment", func() {
		array := []string{"one", "two", "three"}
		list := list.New()
		list.PushBack("one")
		list.PushBack("two")
		list.PushBack("three")

		c.Expect(array, Contains, "one")
		c.Expect(list, Contains, "two")
		c.Expect(list.Iter(), Contains, "three")
		c.Expect(array, Not(Contains), "four")

		c.Expect(list, ContainsAll, Values("two", "one"))
		c.Expect(list, ContainsAny, Values("apple", "orange", "one"))
		c.Expect(list, ContainsExactly, Values("two", "one", "three"))
		c.Expect(list, ContainsInOrder, Values("one", "two", "three"))
		c.Expect(list, ContainsInPartialOrder, Values("one", "three"))
	})
}

func HasSameLengthAs(actual interface{}, expected interface{}) (ok bool, pos os.Error, neg os.Error, err os.Error) {
	ok = len(actual.(string)) == len(expected.(string))
	pos = Errorf("'%v' should have same length as '%v'", actual, expected)
	neg = Errorf("'%v' should NOT have same length as '%v'", actual, expected)
	return
}

<commit_msg>Go allows importing a package many times with different names, so use it until GoSpec is divided into packages<commit_after>// Copyright © 2009-2010 Esko Luontola <www.orfjackal.net>
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0

package examples

import (
	"container/list"
	"gospec"
	. "gospec"
	"os"
)

func ExpectationExamplesSpec(c gospec.Context) {

	c.Specify("Objects can be compared for equality", func() {
		c.Expect(1, Equals, 1)
		c.Expect("string", Equals, "string")

		// There are some shorthands for commonly used comparisons:
		c.Expect(true, IsTrue)
		c.Expect(false, IsFalse)
		c.Expect(nil, IsNil)
		var typedNilPointerInsideInterfaceValue *os.File
		c.Expect(typedNilPointerInsideInterfaceValue, IsNil)

		// Comparing pointer equality is also possible:
		p1 := &Point2{1, 2}
		p2 := p1
		p3 := &Point2{1, 2}
		c.Expect(p2, IsSame, p1)
		c.Expect(p3, Not(IsSame), p1)

		// Comparing floats for equality is not recommended, because
		// floats are rarely exactly equal. So don't write like this:
		c.Expect(3.141, Equals, 3.141)
		// But instead compare using a delta and write like this:
		c.Expect(3.141, IsWithin(0.001), 3.1415926535)

		// Objects with an "Equals(interface{}) bool" method can be
		// compared for equality. See "point.go" for details of how
		// the Equals(interface{}) method should be written. Special
		// care is needed if the objects are used both as values and
		// as pointers.
		a1 := Point2{1, 2}
		a2 := Point2{1, 2}
		c.Expect(a1, Equals, a2)

		b1 := &Point3{1, 2, 3}
		b2 := &Point3{1, 2, 3}
		c.Expect(b1, Equals, b2)
	})

	c.Specify("All expectations can be negated", func() {
		c.Expect(1, Not(Equals), 2)
		c.Expect("apples", Not(Equals), "oranges")
		c.Expect(new(int), Not(IsNil))
	})

	c.Specify("Boolean expressions can be stated about an object", func() {
		s := "some string"
		c.Expect(s, Satisfies, len(s) >= 10 && len(s) <= 20)
		c.Expect(s, Not(Satisfies), len(s) == 0)
	})

	c.Specify("Custom matchers can be defined for commonly used expressions", func() {
		s1 := "first string"
		s2 := "other string"
		c.Expect(s1, HasSameLengthAs, s2)
	})

	c.Specify("Arrays, slices, iterables and channels can be tested for containment", func() {
		array := []string{"one", "two", "three"}
		list := list.New()
		list.PushBack("one")
		list.PushBack("two")
		list.PushBack("three")

		c.Expect(array, Contains, "one")
		c.Expect(list, Contains, "two")
		c.Expect(list.Iter(), Contains, "three")
		c.Expect(array, Not(Contains), "four")

		c.Expect(list, ContainsAll, Values("two", "one"))
		c.Expect(list, ContainsAny, Values("apple", "orange", "one"))
		c.Expect(list, ContainsExactly, Values("two", "one", "three"))
		c.Expect(list, ContainsInOrder, Values("one", "two", "three"))
		c.Expect(list, ContainsInPartialOrder, Values("one", "three"))
	})
}

func HasSameLengthAs(actual interface{}, expected interface{}) (ok bool, pos os.Error, neg os.Error, err os.Error) {
	ok = len(actual.(string)) == len(expected.(string))
	pos = Errorf("'%v' should have same length as '%v'", actual, expected)
	neg = Errorf("'%v' should NOT have same length as '%v'", actual, expected)
	return
}

<|endoftext|>


<commit_before>package epochs

import (
	"testing"
	"time"
)

var MixedTests = []struct {
	f   func(int64) time.Time
	num int64
	exp time.Time
}{
	{
		Chrome,
		12879041490000000,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
	},
	{
		Chrome,
		12912187816559001,
		time.Date(2010, time.March, 4, 14, 50, 16, 559001000, time.UTC),
	},
	{
		Cocoa,
		256260690,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
	},
	{
		Cocoa,
		314238233,
		time.Date(2010, time.December, 17, 0, 23, 53, 0, time.UTC),
	},
	{
		GoogleCalendar,
		1297899090,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
	},
	{
		GoogleCalendar,
		1234567890,
		time.Date(2007, time.March, 16, 23, 31, 30, 0, time.UTC),
	},
	{
		Java,
		1234567890000,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
	},
	{
		Java,
		1283002533751,
		time.Date(2010, time.August, 28, 13, 35, 33, 751000000, time.UTC),
	},
	{
		Mozilla,
		1234567890000000,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
	},
	{
		Symbian,
		63401787090000000,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
	},
	{
		Unix,
		1234567890,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
	},
	{
		Unix,
		-1234567890,
		time.Date(1930, time.November, 18, 0, 28, 30, 0, time.UTC),
	},
	{
		UUIDv1,
		134538606900000000,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
	},
	{
		UUIDv1,
		0x1dc7711a73088f5,
		time.Date(2007, time.October, 10, 9, 17, 41, 739749300, time.UTC),
	},
	{
		WindowsDate,
		633701646900000000,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
	},
	{
		WindowsDate,
		634496538123456789,
		time.Date(2011, time.August, 22, 23, 50, 12, 345678900, time.UTC),
	},
	{
		WindowsFile,
		128790414900000000,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
	},
	{
		WindowsFile,
		0x1cabbaa00ca9000,
		time.Date(2010, time.March, 4, 14, 50, 16, 559001600, time.UTC),
	},
}

func TestMixed(t *testing.T) {
	for _, tt := range MixedTests {

		obs := tt.f(tt.num)
		if obs != tt.exp {
			t.Errorf("%q(%q) => %q, want %q", tt.f, tt.num, obs, tt.exp)
		}
	}
}

var ICQTests = []struct {
	f   func(float64) time.Time
	num float64
	exp time.Time
}{
	{
		ICQ,
		0,
		time.Date(1899, time.December, 30, 0, 0, 0, 0, time.UTC),
	},
	{
		ICQ,
		41000,
		time.Date(2012, time.April, 1, 0, 0, 0, 0, time.UTC),
	},
	{
		ICQ,
		41056.2752083333,
		time.Date(2012, time.May, 27, 6, 36, 17, 999997418, time.UTC),
	},
	{
		ICQ,
		41056.2967361111,
		time.Date(2012, time.May, 27, 7, 7, 17, 999999080, time.UTC),
	},
}

func TestICQ(t *testing.T) {
	for _, tt := range ICQTests {

		obs := tt.f(tt.num)
		if obs != tt.exp {
			t.Errorf("%q(%q) => %q, want %q", tt.f, tt.num, obs, tt.exp)
		}
	}
}

var OLETests = []struct {
	f   func(string) time.Time
	num string
	exp time.Time
}{
	{
		OLE,
		"dedddd5d3f76e340",
		time.Date(2009, time.February, 13, 23, 31, 30, 83, time.UTC),
	},
	{
		OLE,
		"8ad371b4bcd2e340",
		time.Date(2011, time.February, 23, 21, 31, 43, 127000061, time.UTC),
	},
}

func TestOLE(t *testing.T) {
	for _, tt := range OLETests {
		obs := tt.f(tt.num)
		if obs != tt.exp {
			t.Errorf("%q(%q) => %q, want %q", tt.f, tt.num, obs, tt.exp)
		}
	}
}
<commit_msg>no function name<commit_after>package epochs

import (
	"testing"
	"time"
)

var EpochsTests = []struct {
	f   func(int64) time.Time
	num int64
	exp time.Time
}{
	{
		Chrome,
		12879041490000000,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
	},
	{
		Chrome,
		12912187816559001,
		time.Date(2010, time.March, 4, 14, 50, 16, 559001000, time.UTC),
	},
	{
		Cocoa,
		256260690,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
	},
	{
		Cocoa,
		314238233,
		time.Date(2010, time.December, 17, 0, 23, 53, 0, time.UTC),
	},
	{
		GoogleCalendar,
		1297899090,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
	},
	{
		GoogleCalendar,
		1234567890,
		time.Date(2007, time.March, 16, 23, 31, 30, 0, time.UTC),
	},
	{
		Java,
		1234567890000,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
	},
	{
		Java,
		1283002533751,
		time.Date(2010, time.August, 28, 13, 35, 33, 751000000, time.UTC),
	},
	{
		Mozilla,
		1234567890000000,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
	},
	{
		Symbian,
		63401787090000000,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
	},
	{
		Unix,
		1234567890,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
	},
	{
		Unix,
		-1234567890,
		time.Date(1930, time.November, 18, 0, 28, 30, 0, time.UTC),
	},
	{
		UUIDv1,
		134538606900000000,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
	},
	{
		UUIDv1,
		0x1dc7711a73088f5,
		time.Date(2007, time.October, 10, 9, 17, 41, 739749300, time.UTC),
	},
	{
		WindowsDate,
		633701646900000000,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
	},
	{
		WindowsDate,
		634496538123456789,
		time.Date(2011, time.August, 22, 23, 50, 12, 345678900, time.UTC),
	},
	{
		WindowsFile,
		128790414900000000,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
	},
	{
		WindowsFile,
		0x1cabbaa00ca9000,
		time.Date(2010, time.March, 4, 14, 50, 16, 559001600, time.UTC),
	},
}

func TestEpochs(t *testing.T) {
	for _, tt := range EpochsTests {

		obs := tt.f(tt.num)
		if obs != tt.exp {
			t.Errorf("%q => %q, want %q", tt.num, obs, tt.exp)
		}
	}
}
<|endoftext|>


<commit_before>/*

errors is a package that implements FriendlyError. FriendlyError implements
the error interface, but also has a FriendlyError() method that returns a
message that is reasonable to show to end-users.

Every method that returns a FriendlyError also accepts 0 to n Fields objects
may be provided and they will be combined into a single Fields, with later
values overwriting newer ones.

FriendlyErrors also have With* methods that return a copy of the error with
the given changes. This allows a pattern where a method defines a default
FriendlyError with the friendly error message to use and then when it needs to
return an error uses WithError() to add the specific error message.

*/
package errors

const DefaultFriendlyError = "An error occurred"

type Fields map[string]interface{}

type Friendly struct {
	msg         string
	friendlyMsg string
	secureMsg   string
	fields      Fields
}

//New creates a new errors.Friendly with the given msg.
func New(msg string, fields ...Fields) *Friendly {
	return &Friendly{
		msg:    msg,
		fields: combineFields(fields...),
	}
}

//NewFriendly returns a new errors.Friendly with the given FriendlyError.
func NewFriendly(friendlyMsg string, fields ...Fields) *Friendly {
	return &Friendly{
		friendlyMsg: friendlyMsg,
		fields:      combineFields(fields...),
	}
}

//NewSecure returns a new errors.Friendly with the given SecureError.
func NewSecure(secureMsg string, fields ...Fields) *Friendly {
	return &Friendly{
		secureMsg: secureMsg,
		fields:    combineFields(fields...),
	}
}

//NewWrapped takes a generic error. It returns either a new error with that
//message or, if nil, returns nil.
func NewWrapped(err error, fields ...Fields) *Friendly {
	if err == nil {
		return nil
	}
	return New(err.Error(), fields...)
}

func combineFields(fields ...Fields) Fields {
	var result = make(Fields)
	for _, field := range fields {
		for key, val := range field {
			result[key] = val
		}
	}
	return result
}

//SecureError returns the error message that should only be shown in secure
//contexts, because it may include secret information. If no SecureError
//message has been provided, will return Error()
func (f *Friendly) SecureError() string {
	if f.secureMsg == "" {
		return f.Error()
	}
	return f.secureMsg
}

//Error returns the error message, implementing the error interface. If no
//specific message has been provided, will fall back on FriendlyError(). The
//Error() value is OK to show in insecure contexts (i.e. on the client) but it
//just might be confusing to users.
func (f *Friendly) Error() string {
	if f.msg == "" {
		return f.FriendlyError()
	}
	return f.msg
}

//FriendlyError is the error string that is OK to show in insecure contexts to
//end-users. It is generally a much simplified version of the message. If no
//specific FriendlyMessage has been provided, will return DefaultFriendlyError.
func (f *Friendly) FriendlyError() string {
	if f.friendlyMsg == "" {
		return DefaultFriendlyError
	}
	return f.friendlyMsg
}

//Fields returns the Fields object for this error. Fields should only be used
//in secure contexts (e.g. logging on a server) because they may contain
//private information.
func (f *Friendly) Fields() Fields {
	return f.fields
}

//Extend returns a new FriendlyError where the Error() message is prepended
//with this new message and a delimiter. The SecureError and FriendlyError
//message are left untouched.
func (f *Friendly) Extend(msg string, fields ...Fields) *Friendly {
	return &Friendly{
		secureMsg:   f.secureMsg,
		friendlyMsg: f.friendlyMsg,
		msg:         msg + " : " + f.Error(),
		fields:      combineFields(append([]Fields{f.fields}, fields...)...),
	}
}

//WithFriendly returns a copy of err where the friendlyMsg is set to friendlyMsg
func (f *Friendly) WithFriendly(friendlyMsg string, fields ...Fields) *Friendly {
	return &Friendly{
		secureMsg:   f.secureMsg,
		msg:         f.msg,
		friendlyMsg: friendlyMsg,
		fields:      combineFields(append([]Fields{f.fields}, fields...)...),
	}
}

//WithError returns a copy of err where the Error() is set to msg. See a;so
//Extend, which prepends a new message to the front of the existing message.
func (f *Friendly) WithError(msg string, fields ...Fields) *Friendly {
	return &Friendly{
		secureMsg:   f.secureMsg,
		msg:         msg,
		friendlyMsg: f.friendlyMsg,
		fields:      combineFields(append([]Fields{f.fields}, fields...)...),
	}
}

//WithSecure returns a copy of err where the SecureError() is set to secureMsg.
func (f *Friendly) WithSecure(secureMsg string, fields ...Fields) *Friendly {
	return &Friendly{
		secureMsg:   secureMsg,
		msg:         f.msg,
		friendlyMsg: f.friendlyMsg,
		fields:      combineFields(append([]Fields{f.fields}, fields...)...),
	}
}
<commit_msg>NewWrapped returns a generic error in its signature so the nil it returns won't be a non-nil interface value. See https://golang.org/doc/faq#nil_error. Part of #243.<commit_after>/*

errors is a package that implements FriendlyError. FriendlyError implements
the error interface, but also has a FriendlyError() method that returns a
message that is reasonable to show to end-users.

Every method that returns a FriendlyError also accepts 0 to n Fields objects
may be provided and they will be combined into a single Fields, with later
values overwriting newer ones.

FriendlyErrors also have With* methods that return a copy of the error with
the given changes. This allows a pattern where a method defines a default
FriendlyError with the friendly error message to use and then when it needs to
return an error uses WithError() to add the specific error message.

*/
package errors

const DefaultFriendlyError = "An error occurred"

type Fields map[string]interface{}

type Friendly struct {
	msg         string
	friendlyMsg string
	secureMsg   string
	fields      Fields
}

//New creates a new errors.Friendly with the given msg.
func New(msg string, fields ...Fields) *Friendly {
	return &Friendly{
		msg:    msg,
		fields: combineFields(fields...),
	}
}

//NewFriendly returns a new errors.Friendly with the given FriendlyError.
func NewFriendly(friendlyMsg string, fields ...Fields) *Friendly {
	return &Friendly{
		friendlyMsg: friendlyMsg,
		fields:      combineFields(fields...),
	}
}

//NewSecure returns a new errors.Friendly with the given SecureError.
func NewSecure(secureMsg string, fields ...Fields) *Friendly {
	return &Friendly{
		secureMsg: secureMsg,
		fields:    combineFields(fields...),
	}
}

//NewWrapped takes a generic error. It returns either a new error with that
//message or, if nil, returns nil. Returns a generic error so if it returns
//nil it will no be a nil interface value to a *Friendly.
func NewWrapped(err error, fields ...Fields) error {
	if err == nil {
		return nil
	}
	return New(err.Error(), fields...)
}

func combineFields(fields ...Fields) Fields {
	var result = make(Fields)
	for _, field := range fields {
		for key, val := range field {
			result[key] = val
		}
	}
	return result
}

//SecureError returns the error message that should only be shown in secure
//contexts, because it may include secret information. If no SecureError
//message has been provided, will return Error()
func (f *Friendly) SecureError() string {
	if f.secureMsg == "" {
		return f.Error()
	}
	return f.secureMsg
}

//Error returns the error message, implementing the error interface. If no
//specific message has been provided, will fall back on FriendlyError(). The
//Error() value is OK to show in insecure contexts (i.e. on the client) but it
//just might be confusing to users.
func (f *Friendly) Error() string {
	if f.msg == "" {
		return f.FriendlyError()
	}
	return f.msg
}

//FriendlyError is the error string that is OK to show in insecure contexts to
//end-users. It is generally a much simplified version of the message. If no
//specific FriendlyMessage has been provided, will return DefaultFriendlyError.
func (f *Friendly) FriendlyError() string {
	if f.friendlyMsg == "" {
		return DefaultFriendlyError
	}
	return f.friendlyMsg
}

//Fields returns the Fields object for this error. Fields should only be used
//in secure contexts (e.g. logging on a server) because they may contain
//private information.
func (f *Friendly) Fields() Fields {
	return f.fields
}

//Extend returns a new FriendlyError where the Error() message is prepended
//with this new message and a delimiter. The SecureError and FriendlyError
//message are left untouched.
func (f *Friendly) Extend(msg string, fields ...Fields) *Friendly {
	return &Friendly{
		secureMsg:   f.secureMsg,
		friendlyMsg: f.friendlyMsg,
		msg:         msg + " : " + f.Error(),
		fields:      combineFields(append([]Fields{f.fields}, fields...)...),
	}
}

//WithFriendly returns a copy of err where the friendlyMsg is set to friendlyMsg
func (f *Friendly) WithFriendly(friendlyMsg string, fields ...Fields) *Friendly {
	return &Friendly{
		secureMsg:   f.secureMsg,
		msg:         f.msg,
		friendlyMsg: friendlyMsg,
		fields:      combineFields(append([]Fields{f.fields}, fields...)...),
	}
}

//WithError returns a copy of err where the Error() is set to msg. See a;so
//Extend, which prepends a new message to the front of the existing message.
func (f *Friendly) WithError(msg string, fields ...Fields) *Friendly {
	return &Friendly{
		secureMsg:   f.secureMsg,
		msg:         msg,
		friendlyMsg: f.friendlyMsg,
		fields:      combineFields(append([]Fields{f.fields}, fields...)...),
	}
}

//WithSecure returns a copy of err where the SecureError() is set to secureMsg.
func (f *Friendly) WithSecure(secureMsg string, fields ...Fields) *Friendly {
	return &Friendly{
		secureMsg:   secureMsg,
		msg:         f.msg,
		friendlyMsg: f.friendlyMsg,
		fields:      combineFields(append([]Fields{f.fields}, fields...)...),
	}
}
<|endoftext|>


<commit_before>package main

import (
	"image/color"
	"math"
	"math/rand"

	oak "github.com/oakmound/oak/v3"
	"github.com/oakmound/oak/v3/collision"
	"github.com/oakmound/oak/v3/entities"
	"github.com/oakmound/oak/v3/event"
	"github.com/oakmound/oak/v3/key"
	"github.com/oakmound/oak/v3/render"
	"github.com/oakmound/oak/v3/scene"
)

var (
	score1 = 0
	score2 = 0
)

const (
	hitPaddle collision.Label = 1
)

func main() {
	oak.AddScene("pong",
		scene.Scene{Start: func(ctx *scene.Context) {
			newPaddle(ctx, 20, 200, 1)
			newPaddle(ctx, 600, 200, 2)
			newBall(ctx, 320, 240)
			render.Draw(render.DefaultFont().NewIntText(&score2, 200, 20), 3)
			render.Draw(render.DefaultFont().NewIntText(&score1, 400, 20), 3)
		}})
	oak.Init("pong", func(c oak.Config) (oak.Config, error) {
		c.DrawFrameRate = 120
		return c, nil
	})
}

func newBall(ctx *scene.Context, x, y float64) {
	b := entities.NewMoving(x, y, 10, 10, render.NewColorBoxR(10, 10, color.RGBA{255, 255, 255, 255}), nil, 0, 0)
	render.Draw(b.R, 2)
	event.GlobalBind(ctx, event.Enter, func(_ event.EnterPayload) event.Response {
		if b.Delta.X() == 0 && b.Delta.Y() == 0 {
			b.Delta.SetY((rand.Float64() - 0.5) * 4)
			b.Delta.SetX((rand.Float64() - 0.5) * 16)
			if math.Abs(b.Delta.X()) < 0.1 {
				b.Delta.SetX(8)
			}
		}
		b.ShiftPos(b.Delta.X(), b.Delta.Y())
		if collision.HitLabel(b.Space, hitPaddle) != nil {
			b.Delta.SetX(-1.1 * b.Delta.X())
			b.Delta.SetY(b.Delta.Y() + (rand.Float64()-0.5)*8)
		}
		if b.X() < 0 || b.X() > 640 {
			if b.X() < 0 {
				score1++
			} else {
				score2++
			}
			b.Delta.SetX(0)
			b.Delta.SetY(0)
			b.SetPos(320, 240)
		} else if b.Y() < 0 || b.Y() > 480-b.H {
			b.Delta.SetY(-1 * b.Delta.Y())
		}
		return 0
	})
}

func newPaddle(ctx *scene.Context, x, y float64, player int) {
	p := entities.NewMoving(x, y, 20, 100, render.NewColorBoxR(20, 100, color.RGBA{255, 255, 255, 255}), nil, 0, 0)
	p.Speed.SetY(8)
	render.Draw(p.R, 1)
	p.Space.UpdateLabel(hitPaddle)
	if player == 1 {
		event.Bind(ctx, event.Enter, p, enterPaddle(key.UpArrow, key.DownArrow))
	} else {
		event.Bind(ctx, event.Enter, p, enterPaddle(key.W, key.S))
	}
}

func enterPaddle(up, down key.Code) func(*entities.Moving, event.EnterPayload) event.Response {
	return func(p *entities.Moving, _ event.EnterPayload) event.Response {
		p.Delta.SetY(0)
		if oak.IsDown(up) {
			p.Delta.SetY(-p.Speed.Y())
		} else if oak.IsDown(down) {
			p.Delta.SetY(p.Speed.Y())
		}
		p.ShiftY(p.Delta.Y())
		if p.Y() < 0 || p.Y() > (480-p.H) {
			p.ShiftY(-p.Delta.Y())
		}
		return 0
	}
}
<commit_msg>examples/pong: rewrite for entities v4<commit_after>package main

import (
	"image/color"
	"math"
	"math/rand"

	oak "github.com/oakmound/oak/v3"
	"github.com/oakmound/oak/v3/alg/floatgeom"
	"github.com/oakmound/oak/v3/collision"
	"github.com/oakmound/oak/v3/entities"
	"github.com/oakmound/oak/v3/event"
	"github.com/oakmound/oak/v3/key"
	"github.com/oakmound/oak/v3/render"
	"github.com/oakmound/oak/v3/scene"
)

var (
	score1 = 0
	score2 = 0
)

const (
	hitPaddle collision.Label = 1
)

func main() {
	oak.AddScene("pong",
		scene.Scene{Start: func(ctx *scene.Context) {
			newPaddle(ctx, 20, 200, 1)
			newPaddle(ctx, 600, 200, 2)
			newBall(ctx, 320, 240)
			ctx.Draw(render.NewIntText(&score2, 200, 20), 3)
			ctx.Draw(render.NewIntText(&score1, 400, 20), 3)
		}})
	oak.Init("pong")
}

func newBallDelta() floatgeom.Point2 {
	d := floatgeom.Point2{(rand.Float64() - 0.5) * 4, (rand.Float64() - 0.5) * 16}
	if math.Abs(d.X()) < 0.5 {
		d[0] *= 5
	}
	return d
}

func newBall(ctx *scene.Context, x, y float64) {
	ball := entities.New(ctx,
		entities.WithRect(floatgeom.NewRect2WH(x, y, 10, 10)),
		entities.WithColor(color.RGBA{255, 255, 255, 255}),
		entities.WithDrawLayers([]int{2}),
	)
	ball.Delta = newBallDelta()
	event.Bind(ctx, event.Enter, ball, func(ball *entities.Entity, _ event.EnterPayload) event.Response {
		ball.ShiftDelta()
		if collision.HitLabel(ball.Space, hitPaddle) != nil {
			ball.Delta[0] *= -1.1
			ball.Delta[1] += (rand.Float64() - 0.5) * 8
		}
		if ball.X() < 0 || ball.X() > 640 {
			if ball.X() < 0 {
				score1++
			} else {
				score2++
			}
			ball.Delta = newBallDelta()
			ball.SetPos(floatgeom.Point2{320, 240})
		} else if ball.Y() < 0 || ball.Y() > 480-ball.H() {
			ball.Delta[1] = -1 * ball.Delta.Y()
		}
		return 0
	})
}

func newPaddle(ctx *scene.Context, x, y float64, player int) {
	paddle := entities.New(ctx,
		entities.WithRect(floatgeom.NewRect2WH(x, y, 20, 100)),
		entities.WithColor(color.RGBA{255, 255, 255, 255}),
		entities.WithDrawLayers([]int{1}),
		entities.WithLabel(hitPaddle),
	)
	if player == 2 {
		event.Bind(ctx, event.Enter, paddle, enterPaddle(key.UpArrow, key.DownArrow))
	} else {
		event.Bind(ctx, event.Enter, paddle, enterPaddle(key.W, key.S))
	}
}

func enterPaddle(up, down key.Code) func(*entities.Entity, event.EnterPayload) event.Response {
	return func(p *entities.Entity, _ event.EnterPayload) event.Response {
		if oak.IsDown(up) {
			if p.Y() > 0 {
				p.ShiftY(-8)
			}
		} else if oak.IsDown(down) && p.Y() < (480-p.H()) {
			p.ShiftY(8)
		}
		return 0
	}
}
<|endoftext|>


<commit_before>package sudoku

import (
	"testing"
	"time"
)

func BenchmarkHumanSolve(b *testing.B) {
	for i := 0; i < b.N; i++ {
		grid := NewGrid()
		grid.Load(TEST_GRID)
		grid.HumanSolve()
		grid.Done()
	}
}

func TestHumanSolve(t *testing.T) {
	grid := NewGrid()
	grid.Load(TEST_GRID)

	steps := grid.HumanSolution()

	if steps == nil {
		t.Log("Human solution returned 0 techniques.")
		t.Fail()
	}

	if grid.Solved() {
		t.Log("Human Solutions mutated the grid.")
		t.Fail()
	}

	steps = grid.HumanSolve()
	//TODO: test to make sure that we use a wealth of different techniques. This will require a cooked random for testing.
	if steps == nil {
		t.Log("Human solve returned 0 techniques")
		t.Fail()
	}
	if !grid.Solved() {
		t.Log("Human solve failed to solve the simple grid.")
		t.Fail()
	}

	grid.Done()

}

func TestHumanSolveWithGuess(t *testing.T) {
	grid := NewGrid()

	grid.LoadFromFile(puzzlePath("harddificulty.sdk"))

	steps := grid.HumanSolution()

	if steps == nil {
		t.Fatal("Didn't find a solution to a grid that should have needed a guess")
	}

	foundGuess := false
	for i, step := range steps {
		if step.Technique.Name() == "Guess" {
			foundGuess = true
		}
		step.Apply(grid)
		if grid.Invalid() {
			t.Fatal("A solution with a guess in it got us into an invalid grid state. step", i)
		}
	}

	if !foundGuess {
		t.Error("Solution that should have used guess didn't have any guess.")
	}

	if !grid.Solved() {
		t.Error("A solution with a guess said it should solve the puzzle, but it didn't.")
	}

}

func TestStepsDescription(t *testing.T) {

	grid := NewGrid()

	//It's really brittle that we load techniques in this way... it changes every time we add a new early technique!
	steps := SolveDirections{
		&SolveStep{
			CellList{
				grid.Cell(0, 0),
			},
			nil,
			IntSlice{1},
			nil,
			Techniques[3],
		},
		&SolveStep{
			CellList{
				grid.Cell(1, 0),
				grid.Cell(1, 1),
			},
			CellList{
				grid.Cell(1, 3),
				grid.Cell(1, 4),
			},
			IntSlice{1, 2},
			nil,
			Techniques[8],
		},
		&SolveStep{
			CellList{
				grid.Cell(2, 0),
			},
			nil,
			IntSlice{2},
			nil,
			Techniques[3],
		},
	}

	descriptions := steps.Description()

	GOLDEN_DESCRIPTIONS := []string{
		"First, we put 1 in cell (0,0) because 1 is the only remaining valid number for that cell.",
		"Next, we remove the possibilities 1 and 2 from cells (1,0) and (1,1) because 1 is only possible in column 0 of block 1, which means it can't be in any other cell in that column not in that block.",
		"Finally, we put 2 in cell (2,0) because 2 is the only remaining valid number for that cell.",
	}

	for i := 0; i < len(GOLDEN_DESCRIPTIONS); i++ {
		if descriptions[i] != GOLDEN_DESCRIPTIONS[i] {
			t.Log("Got wrong human solve description: ", descriptions[i])
			t.Fail()
		}
	}
}

func TestPuzzleDifficulty(t *testing.T) {
	grid := NewGrid()
	grid.Load(TEST_GRID)

	difficulty := grid.Difficulty()

	if grid.Solved() {
		t.Log("Difficulty shouldn't have changed the underlying grid, but it did.")
		t.Fail()
	}

	if difficulty < 0.0 || difficulty > 1.0 {
		t.Log("The grid's difficulty was outside of allowed bounds.")
		t.Fail()
	}

	grid.Done()

	puzzleFilenames := []string{"harddifficulty.sdk", "harddifficulty2.sdk"}

	for _, filename := range puzzleFilenames {
		puzzleDifficultyHelper(filename, t)
	}
}

func puzzleDifficultyHelper(filename string, t *testing.T) {
	otherGrid := NewGrid()
	if !otherGrid.LoadFromFile(puzzlePath(filename)) {
		t.Log("Whoops, couldn't load the file to test:", filename)
		t.Fail()
	}

	after := time.After(time.Second * 5)

	done := make(chan bool)

	go func() {
		_ = otherGrid.Difficulty()
		done <- true
	}()

	select {
	case <-done:
		//totally fine.
	case <-after:
		//Uh oh.
		t.Log("We never finished solving the hard difficulty puzzle: ", filename)
		t.Fail()
	}
}
<commit_msg>TESTS FAIL. Took a note on the next step (the test loads a blank grid!)<commit_after>package sudoku

import (
	"testing"
	"time"
)

func BenchmarkHumanSolve(b *testing.B) {
	for i := 0; i < b.N; i++ {
		grid := NewGrid()
		grid.Load(TEST_GRID)
		grid.HumanSolve()
		grid.Done()
	}
}

func TestHumanSolve(t *testing.T) {
	grid := NewGrid()
	grid.Load(TEST_GRID)

	steps := grid.HumanSolution()

	if steps == nil {
		t.Log("Human solution returned 0 techniques.")
		t.Fail()
	}

	if grid.Solved() {
		t.Log("Human Solutions mutated the grid.")
		t.Fail()
	}

	steps = grid.HumanSolve()
	//TODO: test to make sure that we use a wealth of different techniques. This will require a cooked random for testing.
	if steps == nil {
		t.Log("Human solve returned 0 techniques")
		t.Fail()
	}
	if !grid.Solved() {
		t.Log("Human solve failed to solve the simple grid.")
		t.Fail()
	}

	grid.Done()

}

func TestHumanSolveWithGuess(t *testing.T) {
	grid := NewGrid()

	grid.LoadFromFile(puzzlePath("harddificulty.sdk"))

	//TODO: this grid is empty here. Fix it!

	steps := grid.HumanSolution()

	if steps == nil {
		t.Fatal("Didn't find a solution to a grid that should have needed a guess")
	}

	foundGuess := false
	for i, step := range steps {
		if step.Technique.Name() == "Guess" {
			foundGuess = true
		}
		step.Apply(grid)
		if grid.Invalid() {
			t.Fatal("A solution with a guess in it got us into an invalid grid state. step", i)
		}
	}

	if !foundGuess {
		t.Error("Solution that should have used guess didn't have any guess.")
	}

	if !grid.Solved() {
		t.Error("A solution with a guess said it should solve the puzzle, but it didn't.")
	}

}

func TestStepsDescription(t *testing.T) {

	grid := NewGrid()

	//It's really brittle that we load techniques in this way... it changes every time we add a new early technique!
	steps := SolveDirections{
		&SolveStep{
			CellList{
				grid.Cell(0, 0),
			},
			nil,
			IntSlice{1},
			nil,
			Techniques[3],
		},
		&SolveStep{
			CellList{
				grid.Cell(1, 0),
				grid.Cell(1, 1),
			},
			CellList{
				grid.Cell(1, 3),
				grid.Cell(1, 4),
			},
			IntSlice{1, 2},
			nil,
			Techniques[8],
		},
		&SolveStep{
			CellList{
				grid.Cell(2, 0),
			},
			nil,
			IntSlice{2},
			nil,
			Techniques[3],
		},
	}

	descriptions := steps.Description()

	GOLDEN_DESCRIPTIONS := []string{
		"First, we put 1 in cell (0,0) because 1 is the only remaining valid number for that cell.",
		"Next, we remove the possibilities 1 and 2 from cells (1,0) and (1,1) because 1 is only possible in column 0 of block 1, which means it can't be in any other cell in that column not in that block.",
		"Finally, we put 2 in cell (2,0) because 2 is the only remaining valid number for that cell.",
	}

	for i := 0; i < len(GOLDEN_DESCRIPTIONS); i++ {
		if descriptions[i] != GOLDEN_DESCRIPTIONS[i] {
			t.Log("Got wrong human solve description: ", descriptions[i])
			t.Fail()
		}
	}
}

func TestPuzzleDifficulty(t *testing.T) {
	grid := NewGrid()
	grid.Load(TEST_GRID)

	difficulty := grid.Difficulty()

	if grid.Solved() {
		t.Log("Difficulty shouldn't have changed the underlying grid, but it did.")
		t.Fail()
	}

	if difficulty < 0.0 || difficulty > 1.0 {
		t.Log("The grid's difficulty was outside of allowed bounds.")
		t.Fail()
	}

	grid.Done()

	puzzleFilenames := []string{"harddifficulty.sdk", "harddifficulty2.sdk"}

	for _, filename := range puzzleFilenames {
		puzzleDifficultyHelper(filename, t)
	}
}

func puzzleDifficultyHelper(filename string, t *testing.T) {
	otherGrid := NewGrid()
	if !otherGrid.LoadFromFile(puzzlePath(filename)) {
		t.Log("Whoops, couldn't load the file to test:", filename)
		t.Fail()
	}

	after := time.After(time.Second * 5)

	done := make(chan bool)

	go func() {
		_ = otherGrid.Difficulty()
		done <- true
	}()

	select {
	case <-done:
		//totally fine.
	case <-after:
		//Uh oh.
		t.Log("We never finished solving the hard difficulty puzzle: ", filename)
		t.Fail()
	}
}
<|endoftext|>