<commit_before>// Copyright 2013 The GoMPD Authors. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package mpd

import (
	"container/list"
	"errors"
	"fmt"
	"strconv"
)

type cmdType uint

const (
	cmd_no_return cmdType = iota
	cmd_attr_return
	cmd_id_return
)

type command struct {
	cmd     string
	promise interface{}
	typeOf  cmdType
}

// CommandList is for batch/mass MPD commands.
// See http://mpd.wikia.com/wiki/Music_Player_Daemon_Command_List
// for more details.
type CommandList struct {
	client *Client
	cmdQ   *list.List
}

type PromisedAttrs Attrs
type PromisedId int

// Value is a convience method for ensuring that a promise
// has been computed, returning the Attrs.
func (pa *PromisedAttrs) Value() (Attrs, error) {
	if len(*pa) == 0 {
		return nil, errors.New("This value has not been computed yet.")
	}
	return (Attrs)(*pa), nil
}

// Value is a convience method for ensuring that a promise
// has been computed, returning the ID.
func (pi *PromisedId) Value() (int, error) {
	if *pi == -1 {
		return -1, errors.New("This value has not been computed yet.")
	}
	return (int)(*pi), nil
}

// BeginCommandList creates a new CommandList structure using
// this connection.
func (c *Client) BeginCommandList() *CommandList {
	return &CommandList{c, list.New()}
}

// Ping sends a no-op message to MPD. It's useful for keeping the connection alive.
func (cl *CommandList) Ping() {
	cl.cmdQ.PushBack(&command{"ping", nil, cmd_no_return})
}

// CurrentSong returns information about the current song in the playlist.
func (cl *CommandList) CurrentSong() *PromisedAttrs {
	pa := make(PromisedAttrs)
	cl.cmdQ.PushBack(&command{"currentsong", &pa, cmd_attr_return})
	return &pa
}

// Status returns information about the current status of MPD.
func (cl *CommandList) Status() *PromisedAttrs {
	pa := make(PromisedAttrs)
	cl.cmdQ.PushBack(&command{"status", &pa, cmd_attr_return})
	return &pa
}

//
// Playback control
//

// Next plays next song in the playlist.
func (cl *CommandList) Next() {
	cl.cmdQ.PushBack(&command{"next", nil, cmd_no_return})
}

// Pause pauses playback if pause is true; resumes playback otherwise.
func (cl *CommandList) Pause(pause bool) {
	if pause {
		cl.cmdQ.PushBack(&command{"pause 1", nil, cmd_no_return})
	} else {
		cl.cmdQ.PushBack(&command{"pause 0", nil, cmd_no_return})
	}
}

// Play starts playing the song at playlist position pos. If pos is negative,
// start playing at the current position in the playlist.
func (cl *CommandList) Play(pos int) {
	if pos < 0 {
		cl.cmdQ.PushBack(&command{"play", nil, cmd_no_return})
	} else {
		cl.cmdQ.PushBack(&command{fmt.Sprintf("play %d", pos), nil, cmd_no_return})
	}
}

// PlayId plays the song identified by id. If id is negative, start playing
// at the currect position in playlist.
func (cl *CommandList) PlayId(id int) {
	if id < 0 {
		cl.cmdQ.PushBack(&command{"playid", nil, cmd_no_return})
	} else {
		cl.cmdQ.PushBack(&command{fmt.Sprintf("playid %d", id), nil, cmd_no_return})
	}
}

// Previous plays previous song in the playlist.
func (cl *CommandList) Previous() {
	cl.cmdQ.PushBack(&command{"previous", nil, cmd_no_return})
}

// Seek seeks to the position time (in seconds) of the song at playlist position pos.
func (cl *CommandList) Seek(pos, time int) {
	cl.cmdQ.PushBack(&command{fmt.Sprintf("seek %d %d", pos, time), nil, cmd_no_return})
}

// SeekId is identical to Seek except the song is identified by it's id
// (not position in playlist).
func (cl *CommandList) SeekId(id, time int) {
	cl.cmdQ.PushBack(&command{fmt.Sprintf("seek %d %d", id, time), nil, cmd_no_return})
}

// Stop stops playback.
func (cl *CommandList) Stop() {
	cl.cmdQ.PushBack(&command{"stop", nil, cmd_no_return})
}

// SetVolume sets the MPD volume level.
func (cl *CommandList) SetVolume(volume int) {
	cl.cmdQ.PushBack(&command{fmt.Sprintf("setvol %d", volume), nil, cmd_no_return})
}

//
// Playlist related functions
//

// Delete deletes songs from playlist. If both start and end are positive,
// it deletes those at positions in range [start, end). If end is negative,
// it deletes the song at position start.
func (cl *CommandList) Delete(start, end int) error {
	if start < 0 {
		return errors.New("negative start index")
	}
	if end < 0 {
		cl.cmdQ.PushBack(&command{fmt.Sprintf("delete %d", start), nil, cmd_no_return})
	} else {
		cl.cmdQ.PushBack(&command{fmt.Sprintf("delete %d %d", start, end), nil, cmd_no_return})
	}
	return nil
}

// DeleteId deletes the song identified by id.
func (cl *CommandList) DeleteId(id int) {
	cl.cmdQ.PushBack(&command{fmt.Sprintf("deleteid %d", id), nil, cmd_no_return})
}

// MoveId moves songid to position on the playlist.
func (cl *CommandList) MoveId(songid, position int) {
	cl.cmdQ.PushBack(&command{fmt.Sprintf("moveid %d %d", songid, position), nil, cmd_no_return})
}

// Add adds the file/directory uri to playlist. Directories add recursively.
func (cl *CommandList) Add(uri string) {
	cl.cmdQ.PushBack(&command{fmt.Sprintf("add %q", uri), nil, cmd_no_return})
}

// AddId adds the file/directory uri to playlist and returns the identity
// id of the song added. If pos is positive, the song is added to position
// pos.
func (cl *CommandList) AddId(uri string, pos int) *PromisedId {
	var id PromisedId = -1
	if pos >= 0 {
		cl.cmdQ.PushBack(&command{fmt.Sprintf("addid %q %d", uri, pos), &id, cmd_id_return})
	} else {
		cl.cmdQ.PushBack(&command{fmt.Sprintf("addid %q", uri), &id, cmd_id_return})
	}
	return &id
}

// Clear clears the current playlist.
func (cl *CommandList) Clear() {
	cl.cmdQ.PushBack(&command{"clear", nil, cmd_no_return})
}

// End executes the command list.
func (cl *CommandList) End() error {

	// Tell MPD to start an OK command list:
	beginId, beginErr := cl.client.text.Cmd("command_list_ok_begin")
	if beginErr != nil {
		return beginErr
	}
	cl.client.text.StartResponse(beginId)
	cl.client.text.EndResponse(beginId)

	// Ensure the queue is cleared regardless.
	defer cl.cmdQ.Init()

	// Issue all of the queued up commands in the list:
	for e := cl.cmdQ.Front(); e != nil; e = e.Next() {
		cmdId, cmdErr := cl.client.text.Cmd(e.Value.(*command).cmd)
		if cmdErr != nil {
			return cmdErr
		}
		cl.client.text.StartResponse(cmdId)
		cl.client.text.EndResponse(cmdId)
	}

	// Tell MPD to end the command list and do the operations.
	endId, endErr := cl.client.text.Cmd("command_list_end")
	if endErr != nil {
		return endErr
	}
	cl.client.text.StartResponse(endId)
	defer cl.client.text.EndResponse(endId)

	// Get the responses back and check for errors:
	for e := cl.cmdQ.Front(); e != nil; e = e.Next() {
		switch e.Value.(*command).typeOf {

		case cmd_no_return:
			if err := cl.client.readOKLine("list_OK"); err != nil {
				return err
			}

		case cmd_attr_return:
			a, aErr := cl.client.readAttrs("list_OK")
			if aErr != nil {
				return aErr
			}
			*(e.Value.(*command).promise.(*PromisedAttrs)) = (PromisedAttrs)(a)

		case cmd_id_return:
			a, aErr := cl.client.readAttrs("list_OK")
			if aErr != nil {
				return aErr
			}
			if rid, ridErr := strconv.Atoi(a["Id"]); ridErr != nil {
				return ridErr
			}
			*(e.Value.(*command).promise.(*PromisedId)) = (PromisedId)(rid)

		}
	}

	// Finalize the command list with the last OK:
	if cerr := cl.client.readOKLine("OK"); cerr != nil {
		return cerr
	}

	return nil

}
<commit_msg>fix build<commit_after>// Copyright 2013 The GoMPD Authors. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package mpd

import (
	"container/list"
	"errors"
	"fmt"
	"strconv"
)

type cmdType uint

const (
	cmd_no_return cmdType = iota
	cmd_attr_return
	cmd_id_return
)

type command struct {
	cmd     string
	promise interface{}
	typeOf  cmdType
}

// CommandList is for batch/mass MPD commands.
// See http://mpd.wikia.com/wiki/Music_Player_Daemon_Command_List
// for more details.
type CommandList struct {
	client *Client
	cmdQ   *list.List
}

type PromisedAttrs Attrs
type PromisedId int

// Value is a convience method for ensuring that a promise
// has been computed, returning the Attrs.
func (pa *PromisedAttrs) Value() (Attrs, error) {
	if len(*pa) == 0 {
		return nil, errors.New("This value has not been computed yet.")
	}
	return (Attrs)(*pa), nil
}

// Value is a convience method for ensuring that a promise
// has been computed, returning the ID.
func (pi *PromisedId) Value() (int, error) {
	if *pi == -1 {
		return -1, errors.New("This value has not been computed yet.")
	}
	return (int)(*pi), nil
}

// BeginCommandList creates a new CommandList structure using
// this connection.
func (c *Client) BeginCommandList() *CommandList {
	return &CommandList{c, list.New()}
}

// Ping sends a no-op message to MPD. It's useful for keeping the connection alive.
func (cl *CommandList) Ping() {
	cl.cmdQ.PushBack(&command{"ping", nil, cmd_no_return})
}

// CurrentSong returns information about the current song in the playlist.
func (cl *CommandList) CurrentSong() *PromisedAttrs {
	pa := make(PromisedAttrs)
	cl.cmdQ.PushBack(&command{"currentsong", &pa, cmd_attr_return})
	return &pa
}

// Status returns information about the current status of MPD.
func (cl *CommandList) Status() *PromisedAttrs {
	pa := make(PromisedAttrs)
	cl.cmdQ.PushBack(&command{"status", &pa, cmd_attr_return})
	return &pa
}

//
// Playback control
//

// Next plays next song in the playlist.
func (cl *CommandList) Next() {
	cl.cmdQ.PushBack(&command{"next", nil, cmd_no_return})
}

// Pause pauses playback if pause is true; resumes playback otherwise.
func (cl *CommandList) Pause(pause bool) {
	if pause {
		cl.cmdQ.PushBack(&command{"pause 1", nil, cmd_no_return})
	} else {
		cl.cmdQ.PushBack(&command{"pause 0", nil, cmd_no_return})
	}
}

// Play starts playing the song at playlist position pos. If pos is negative,
// start playing at the current position in the playlist.
func (cl *CommandList) Play(pos int) {
	if pos < 0 {
		cl.cmdQ.PushBack(&command{"play", nil, cmd_no_return})
	} else {
		cl.cmdQ.PushBack(&command{fmt.Sprintf("play %d", pos), nil, cmd_no_return})
	}
}

// PlayId plays the song identified by id. If id is negative, start playing
// at the currect position in playlist.
func (cl *CommandList) PlayId(id int) {
	if id < 0 {
		cl.cmdQ.PushBack(&command{"playid", nil, cmd_no_return})
	} else {
		cl.cmdQ.PushBack(&command{fmt.Sprintf("playid %d", id), nil, cmd_no_return})
	}
}

// Previous plays previous song in the playlist.
func (cl *CommandList) Previous() {
	cl.cmdQ.PushBack(&command{"previous", nil, cmd_no_return})
}

// Seek seeks to the position time (in seconds) of the song at playlist position pos.
func (cl *CommandList) Seek(pos, time int) {
	cl.cmdQ.PushBack(&command{fmt.Sprintf("seek %d %d", pos, time), nil, cmd_no_return})
}

// SeekId is identical to Seek except the song is identified by it's id
// (not position in playlist).
func (cl *CommandList) SeekId(id, time int) {
	cl.cmdQ.PushBack(&command{fmt.Sprintf("seek %d %d", id, time), nil, cmd_no_return})
}

// Stop stops playback.
func (cl *CommandList) Stop() {
	cl.cmdQ.PushBack(&command{"stop", nil, cmd_no_return})
}

// SetVolume sets the MPD volume level.
func (cl *CommandList) SetVolume(volume int) {
	cl.cmdQ.PushBack(&command{fmt.Sprintf("setvol %d", volume), nil, cmd_no_return})
}

//
// Playlist related functions
//

// Delete deletes songs from playlist. If both start and end are positive,
// it deletes those at positions in range [start, end). If end is negative,
// it deletes the song at position start.
func (cl *CommandList) Delete(start, end int) error {
	if start < 0 {
		return errors.New("negative start index")
	}
	if end < 0 {
		cl.cmdQ.PushBack(&command{fmt.Sprintf("delete %d", start), nil, cmd_no_return})
	} else {
		cl.cmdQ.PushBack(&command{fmt.Sprintf("delete %d %d", start, end), nil, cmd_no_return})
	}
	return nil
}

// DeleteId deletes the song identified by id.
func (cl *CommandList) DeleteId(id int) {
	cl.cmdQ.PushBack(&command{fmt.Sprintf("deleteid %d", id), nil, cmd_no_return})
}

// MoveId moves songid to position on the playlist.
func (cl *CommandList) MoveId(songid, position int) {
	cl.cmdQ.PushBack(&command{fmt.Sprintf("moveid %d %d", songid, position), nil, cmd_no_return})
}

// Add adds the file/directory uri to playlist. Directories add recursively.
func (cl *CommandList) Add(uri string) {
	cl.cmdQ.PushBack(&command{fmt.Sprintf("add %q", uri), nil, cmd_no_return})
}

// AddId adds the file/directory uri to playlist and returns the identity
// id of the song added. If pos is positive, the song is added to position
// pos.
func (cl *CommandList) AddId(uri string, pos int) *PromisedId {
	var id PromisedId = -1
	if pos >= 0 {
		cl.cmdQ.PushBack(&command{fmt.Sprintf("addid %q %d", uri, pos), &id, cmd_id_return})
	} else {
		cl.cmdQ.PushBack(&command{fmt.Sprintf("addid %q", uri), &id, cmd_id_return})
	}
	return &id
}

// Clear clears the current playlist.
func (cl *CommandList) Clear() {
	cl.cmdQ.PushBack(&command{"clear", nil, cmd_no_return})
}

// End executes the command list.
func (cl *CommandList) End() error {

	// Tell MPD to start an OK command list:
	beginId, beginErr := cl.client.text.Cmd("command_list_ok_begin")
	if beginErr != nil {
		return beginErr
	}
	cl.client.text.StartResponse(beginId)
	cl.client.text.EndResponse(beginId)

	// Ensure the queue is cleared regardless.
	defer cl.cmdQ.Init()

	// Issue all of the queued up commands in the list:
	for e := cl.cmdQ.Front(); e != nil; e = e.Next() {
		cmdId, cmdErr := cl.client.text.Cmd(e.Value.(*command).cmd)
		if cmdErr != nil {
			return cmdErr
		}
		cl.client.text.StartResponse(cmdId)
		cl.client.text.EndResponse(cmdId)
	}

	// Tell MPD to end the command list and do the operations.
	endId, endErr := cl.client.text.Cmd("command_list_end")
	if endErr != nil {
		return endErr
	}
	cl.client.text.StartResponse(endId)
	defer cl.client.text.EndResponse(endId)

	// Get the responses back and check for errors:
	for e := cl.cmdQ.Front(); e != nil; e = e.Next() {
		switch e.Value.(*command).typeOf {

		case cmd_no_return:
			if err := cl.client.readOKLine("list_OK"); err != nil {
				return err
			}

		case cmd_attr_return:
			a, aErr := cl.client.readAttrs("list_OK")
			if aErr != nil {
				return aErr
			}
			*(e.Value.(*command).promise.(*PromisedAttrs)) = (PromisedAttrs)(a)

		case cmd_id_return:
			a, aErr := cl.client.readAttrs("list_OK")
			if aErr != nil {
				return aErr
			}
			rid, ridErr := strconv.Atoi(a["Id"])
			if ridErr != nil {
				return ridErr
			}
			*(e.Value.(*command).promise.(*PromisedId)) = (PromisedId)(rid)

		}
	}

	// Finalize the command list with the last OK:
	if cerr := cl.client.readOKLine("OK"); cerr != nil {
		return cerr
	}

	return nil

}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"log"
	"time"

	"github.com/phil-mansfield/shellfish/cmd/catalog"
	"github.com/phil-mansfield/shellfish/cmd/env"
	"github.com/phil-mansfield/shellfish/cmd/halo"
	"github.com/phil-mansfield/shellfish/cmd/memo"
	"github.com/phil-mansfield/shellfish/io"
	"github.com/phil-mansfield/shellfish/logging"
	"github.com/phil-mansfield/shellfish/parse"
)

const finderCells = 150

// IDConfig contains the configuration fileds for the 'id' mode of the shellfish
// tool.
type IDConfig struct {
	idType                     string
	ids                        []int64
	idStart, idEnd, snap, mult int64

	exclusionStrategy   string
	exclusionRadiusMult float64
}

var _ Mode = &IDConfig{}

// ExampleConfig creates an example id.config file.
func (config *IDConfig) ExampleConfig() string {
	return `[id.config]
#####################
## Required Fields ##
#####################

# Index of the snapshot to be analyzed.
Snap = 100

IDs = 10, 11, 12, 13, 14

#####################
## Optional Fields ##
#####################

# IDType indicates what the input IDs correspond to. It can be set to the
# following modes:
# halo-id - The numeric IDs given in the halo catalog.
# m200m   - The rank of the halos when sorted by M200m.
#
# Defaults to m200m if not set.
# IDType = m200m

# An alternative way of specifying IDs is to select start and end (inclusive)
# ID values. If the IDs variable is not set, both of these values must be set.
#
# IDStart = 10
# IDEnd = 15

# ExclusionStrategy determines how to exclude IDs from the given set. This is
# useful because splashback shells are not particularly meaningful for
# subhalos. It can be set to the following modes:
# none    - No halos are removed
# subhalo - Halos flagged as subhalos in the catalog are removed
# overlap - Halos which have an R200m shell that overlaps with a larger halo's
#           R200m shell are removed
#
# ExclusionStrategy defaults to overlap if not set.
#
# ExclusionStrategy = overlap

# ExclusionRadiusMult is a multiplier of R200m applied for the sake of
# determining exclusions.
#
# ExclusionRadiusMult defaults to 1 if not set.
#
# ExclustionRadiusMult = 1

# Mult is the number of times a given ID should be repeated. This is most useful
# if you want to estimate the scatter in shell measurements for halos with a
# given set of shell parameters.
#
# Mult defaults to 1 if not set.
#
# Mult = 1`
}

// ReadConfig reads in an id.config file into config.
func (config *IDConfig) ReadConfig(fname string) error {

	vars := parse.NewConfigVars("id.config")
	vars.String(&config.idType, "IDType", "m200m")
	vars.Ints(&config.ids, "IDs", []int64{})
	vars.Int(&config.idStart, "IDStart", -1)
	vars.Int(&config.idEnd, "IDEnd", -1)
	vars.Int(&config.mult, "Mult", 1)
	vars.Int(&config.snap, "Snap", -1)
	vars.String(&config.exclusionStrategy, "ExclusionStrategy", "subhalo")
	vars.Float(&config.exclusionRadiusMult, "ExclusionRadiusMult", 1)

	if fname == "" {
		return nil
	}
	if err := parse.ReadConfig(fname, vars); err != nil {
		return err
	}
	return config.validate()
}

// validate checks whether all the fields of config are valid.
func (config *IDConfig) validate() error {
	switch config.idType {
	case "halo-id", "m200m":
	default:
		return fmt.Errorf("The 'IDType' variable is set to '%s', which I "+
			"don't recognize.", config.idType)
	}

	switch config.exclusionStrategy {
	case "none", "subhalo":
	case "overlap":
		if config.exclusionRadiusMult <= 0 {
			return fmt.Errorf("The 'ExclusionRadiusMult' varaible is set to "+
				"%g, but it needs to be positive.", config.exclusionRadiusMult)
		}
	default:
		return fmt.Errorf("The 'ExclusionStrategy' variable is set to '%s', "+
			"which I don't recognize.", config.exclusionStrategy)
	}

	// TODO: Check the ranges of the IDs as well as IDStart and IDEnd
	if len(config.ids) == 0 {
		switch {
		case config.idStart == -1 && config.idEnd == -1:
			return fmt.Errorf("'IDs' variable not set.")
		case config.idStart == -1:
			return fmt.Errorf("'IDStart variable not set.")
		case config.idEnd == -1:
			return fmt.Errorf("'IDEnd' variable not set.")
		case config.idEnd < config.idStart:
			return fmt.Errorf("'IDEnd' variable set to %d, but 'IDStart' "+
				"variable set to %d.", config.idEnd, config.idStart)
		}
	}

	switch {
	case config.snap == -1:
		return fmt.Errorf("'Snap' variable not set.")
	case config.snap < 0:
		return fmt.Errorf("'Snap' variable set to %d.", config.snap)
	}

	if config.mult <= 0 {
		return fmt.Errorf("'Mult' variable set to %d", config.mult)
	}

	return nil
}

// Run executes the ID mode of shellfish tool.
func (config *IDConfig) Run(
	flags []string, gConfig *GlobalConfig, e *env.Environment, stdin []string,
) ([]string, error) {

	if logging.Mode != logging.Nil {
		log.Println(`
##################
## shellfish id ##
##################`,
		)
	}
	var t time.Time
	if logging.Mode == logging.Performance {
		t = time.Now()
	}

	if config.snap == -1 {
		return nil, fmt.Errorf("Either no id.config file was provided or " +
			"the 'Snap' variable wasn't set.")
	}

	if config.snap < gConfig.SnapMin || config.snap > gConfig.SnapMax {
		return nil, fmt.Errorf("'Snap' = %d, but 'SnapMin' = %d and "+
			"'SnapMax = %d'", config.snap, gConfig.SnapMin, gConfig.SnapMax)
	}

	// Get IDs and snapshots

	rawIds := getIDs(config.idStart, config.idEnd, config.ids)

	vars := &halo.VarColumns{
		ID:    int(gConfig.HaloIDColumn),
		X:     int(gConfig.HaloPositionColumns[0]),
		Y:     int(gConfig.HaloPositionColumns[1]),
		Z:     int(gConfig.HaloPositionColumns[2]),
		M200m: int(gConfig.HaloM200mColumn),
	}

	var (
		ids, snaps []int
		buf        io.VectorBuffer
	)

	switch config.idType {
	case "halo-id":
		snaps = make([]int, len(rawIds))
		for i := range snaps {
			snaps[i] = int(config.snap)
		}
		ids = rawIds

		var err error
		buf, err = getVectorBuffer(
			e.ParticleCatalog(snaps[0], 0),
			gConfig.SnapshotType, gConfig.Endianness,
		)
		if err != nil {
			return nil, err
		}
	case "m200m":
		snaps = make([]int, len(rawIds))
		for i := range snaps {
			snaps[i] = int(config.snap)
		}

		var err error
		buf, err = getVectorBuffer(
			e.ParticleCatalog(snaps[0], 0),
			gConfig.SnapshotType, gConfig.Endianness,
		)
		if err != nil {
			return nil, err
		}

		ids, err = convertSortedIDs(rawIds, int(config.snap), vars, buf, e)
		if err != nil {
			return nil, err
		}

	default:
		panic("Impossible")
	}

	// Tag subhalos, if neccessary.
	exclude := make([]bool, len(ids))
	switch config.exclusionStrategy {
	case "none":
	case "subhalo":
		panic("subhalo is not implemented")
	case "overlap":
		var err error
		exclude, err = findOverlapSubs(ids, snaps, vars, buf, e, config)
		if err != nil {
			return nil, err
		}
	}

	// Generate lines
	intCols := [][]int{ids, snaps}
	floatCols := [][]float64{}
	colOrder := []int{0, 1}
	lines := catalog.FormatCols(intCols, floatCols, colOrder)

	// Filter
	fLines := []string{}
	for i := range lines {
		if !exclude[i] {
			fLines = append(fLines, lines[i])
		}
	}

	// Multiply
	mLines := []string{}
	for i := range fLines {
		for j := 0; j < int(config.mult); j++ {
			mLines = append(mLines, fLines[i])
		}
	}

	cString := catalog.CommentString(
		[]string{"ID", "Snapshot"}, []string{}, []int{0, 1}, []int{1, 1},
	)
	mLines = append([]string{cString}, mLines...)

	if logging.Mode == logging.Performance {
		log.Printf("Time: %s", time.Since(t).String())
		log.Printf("Memory:\n%s", logging.MemString())
	}

	return mLines, nil
}

func getIDs(idStart, idEnd int64, ids []int64) []int {
	if idStart != -1 {
		out := make([]int, idEnd-idStart)
		for i := range out {
			out[i] = int(idStart) + i
		}
		return out
	} else {
		out := make([]int, len(ids))
		for i := range out {
			out[i] = int(ids[i])
		}
		return out
	}
}

func convertSortedIDs(
	rawIDs []int, snap int, vars *halo.VarColumns,
	buf io.VectorBuffer, e *env.Environment,
) ([]int, error) {
	maxID := 0
	for _, id := range rawIDs {
		if id > maxID {
			maxID = id
		}
	}

	rids, err := memo.ReadSortedRockstarIDs(snap, maxID, vars, buf, e)
	if err != nil {
		return nil, err
	}

	ids := make([]int, len(rawIDs))
	for i := range ids {
		ids[i] = rids[rawIDs[i]]
	}
	return ids, nil
}

func findOverlapSubs(
	rawIDs, snaps []int, vars *halo.VarColumns,
	buf io.VectorBuffer, e *env.Environment, config *IDConfig,
) ([]bool, error) {
	isSub := make([]bool, len(rawIDs))

	// Group by snapshot.
	snapGroups := make(map[int][]int)
	groupIdxs := make(map[int][]int)
	for i, id := range rawIDs {
		snap := snaps[i]
		snapGroups[snap] = append(snapGroups[snap], id)
		groupIdxs[snap] = append(groupIdxs[snap], i)
	}

	// Load each snapshot.
	hds, _, err := memo.ReadHeaders(snaps[0], buf, e)
	if err != nil {
		return nil, err
	}
	hd := hds[0]

	for snap, group := range snapGroups {
		rids, err := memo.ReadSortedRockstarIDs(snap, -1, vars, buf, e)
		if err != nil {
			return nil, err
		}
		_, xs, ys, zs, _, rs, err := memo.ReadRockstar(snap, rids, vars, buf, e)

		g := halo.NewGrid(finderCells, hd.TotalWidth, len(xs))
		g.Insert(xs, ys, zs)
		sf := halo.NewSubhaloFinder(g)
		sf.FindSubhalos(xs, ys, zs, rs, config.exclusionRadiusMult)

		for i, id := range group {
			origIdx := groupIdxs[snap][i]
			// TODO: Holy linear search, batman! Fix this.
			for j, checkID := range rids {
				if checkID == id {
					isSub[origIdx] = sf.HostCount(j) > 0
					break
				} else if j == len(rids)-1 {
					return nil, fmt.Errorf("ID %d not in halo list.", id)
				}
			}
		}
	}
	return isSub, nil
}
<commit_msg>Fixed default mode for ExclusionStrategy flag.<commit_after>package cmd

import (
	"fmt"
	"log"
	"time"

	"github.com/phil-mansfield/shellfish/cmd/catalog"
	"github.com/phil-mansfield/shellfish/cmd/env"
	"github.com/phil-mansfield/shellfish/cmd/halo"
	"github.com/phil-mansfield/shellfish/cmd/memo"
	"github.com/phil-mansfield/shellfish/io"
	"github.com/phil-mansfield/shellfish/logging"
	"github.com/phil-mansfield/shellfish/parse"
)

const finderCells = 150

// IDConfig contains the configuration fileds for the 'id' mode of the shellfish
// tool.
type IDConfig struct {
	idType                     string
	ids                        []int64
	idStart, idEnd, snap, mult int64

	exclusionStrategy   string
	exclusionRadiusMult float64
}

var _ Mode = &IDConfig{}

// ExampleConfig creates an example id.config file.
func (config *IDConfig) ExampleConfig() string {
	return `[id.config]
#####################
## Required Fields ##
#####################

# Index of the snapshot to be analyzed.
Snap = 100

IDs = 10, 11, 12, 13, 14

#####################
## Optional Fields ##
#####################

# IDType indicates what the input IDs correspond to. It can be set to the
# following modes:
# halo-id - The numeric IDs given in the halo catalog.
# m200m   - The rank of the halos when sorted by M200m.
#
# Defaults to m200m if not set.
# IDType = m200m

# An alternative way of specifying IDs is to select start and end (inclusive)
# ID values. If the IDs variable is not set, both of these values must be set.
#
# IDStart = 10
# IDEnd = 15

# ExclusionStrategy determines how to exclude IDs from the given set. This is
# useful because splashback shells are not particularly meaningful for
# subhalos. It can be set to the following modes:
# none    - No halos are removed
# subhalo - Halos flagged as subhalos in the catalog are removed
# overlap - Halos which have an R200m shell that overlaps with a larger halo's
#           R200m shell are removed
#
# ExclusionStrategy defaults to overlap if not set.
#
# ExclusionStrategy = overlap

# ExclusionRadiusMult is a multiplier of R200m applied for the sake of
# determining exclusions.
#
# ExclusionRadiusMult defaults to 1 if not set.
#
# ExclustionRadiusMult = 1

# Mult is the number of times a given ID should be repeated. This is most useful
# if you want to estimate the scatter in shell measurements for halos with a
# given set of shell parameters.
#
# Mult defaults to 1 if not set.
#
# Mult = 1`
}

// ReadConfig reads in an id.config file into config.
func (config *IDConfig) ReadConfig(fname string) error {

	vars := parse.NewConfigVars("id.config")
	vars.String(&config.idType, "IDType", "m200m")
	vars.Ints(&config.ids, "IDs", []int64{})
	vars.Int(&config.idStart, "IDStart", -1)
	vars.Int(&config.idEnd, "IDEnd", -1)
	vars.Int(&config.mult, "Mult", 1)
	vars.Int(&config.snap, "Snap", -1)
	vars.String(&config.exclusionStrategy, "ExclusionStrategy", "overlap")
	vars.Float(&config.exclusionRadiusMult, "ExclusionRadiusMult", 1)

	if fname == "" {
		return nil
	}
	if err := parse.ReadConfig(fname, vars); err != nil {
		return err
	}

	return config.validate()
}

// validate checks whether all the fields of config are valid.
func (config *IDConfig) validate() error {
	switch config.idType {
	case "halo-id", "m200m":
	default:
		return fmt.Errorf("The 'IDType' variable is set to '%s', which I "+
			"don't recognize.", config.idType)
	}

	switch config.exclusionStrategy {
	case "none", "subhalo":
	case "overlap":
		if config.exclusionRadiusMult <= 0 {
			return fmt.Errorf("The 'ExclusionRadiusMult' varaible is set to "+
				"%g, but it needs to be positive.", config.exclusionRadiusMult)
		}
	default:
		return fmt.Errorf("The 'ExclusionStrategy' variable is set to '%s', "+
			"which I don't recognize.", config.exclusionStrategy)
	}

	// TODO: Check the ranges of the IDs as well as IDStart and IDEnd
	if len(config.ids) == 0 {
		switch {
		case config.idStart == -1 && config.idEnd == -1:
			return fmt.Errorf("'IDs' variable not set.")
		case config.idStart == -1:
			return fmt.Errorf("'IDStart variable not set.")
		case config.idEnd == -1:
			return fmt.Errorf("'IDEnd' variable not set.")
		case config.idEnd < config.idStart:
			return fmt.Errorf("'IDEnd' variable set to %d, but 'IDStart' "+
				"variable set to %d.", config.idEnd, config.idStart)
		}
	}

	switch {
	case config.snap == -1:
		return fmt.Errorf("'Snap' variable not set.")
	case config.snap < 0:
		return fmt.Errorf("'Snap' variable set to %d.", config.snap)
	}

	if config.mult <= 0 {
		return fmt.Errorf("'Mult' variable set to %d", config.mult)
	}

	return nil
}

// Run executes the ID mode of shellfish tool.
func (config *IDConfig) Run(
	flags []string, gConfig *GlobalConfig, e *env.Environment, stdin []string,
) ([]string, error) {

	if logging.Mode != logging.Nil {
		log.Println(`
##################
## shellfish id ##
##################`,
		)
	}
	var t time.Time
	if logging.Mode == logging.Performance {
		t = time.Now()
	}

	if config.snap == -1 {
		return nil, fmt.Errorf("Either no id.config file was provided or " +
			"the 'Snap' variable wasn't set.")
	}

	if config.snap < gConfig.SnapMin || config.snap > gConfig.SnapMax {
		return nil, fmt.Errorf("'Snap' = %d, but 'SnapMin' = %d and "+
			"'SnapMax = %d'", config.snap, gConfig.SnapMin, gConfig.SnapMax)
	}

	// Get IDs and snapshots

	rawIds := getIDs(config.idStart, config.idEnd, config.ids)

	vars := &halo.VarColumns{
		ID:    int(gConfig.HaloIDColumn),
		X:     int(gConfig.HaloPositionColumns[0]),
		Y:     int(gConfig.HaloPositionColumns[1]),
		Z:     int(gConfig.HaloPositionColumns[2]),
		M200m: int(gConfig.HaloM200mColumn),
	}

	var (
		ids, snaps []int
		buf        io.VectorBuffer
	)

	switch config.idType {
	case "halo-id":
		snaps = make([]int, len(rawIds))
		for i := range snaps {
			snaps[i] = int(config.snap)
		}
		ids = rawIds

		var err error
		buf, err = getVectorBuffer(
			e.ParticleCatalog(snaps[0], 0),
			gConfig.SnapshotType, gConfig.Endianness,
		)
		if err != nil {
			return nil, err
		}
	case "m200m":
		snaps = make([]int, len(rawIds))
		for i := range snaps {
			snaps[i] = int(config.snap)
		}

		var err error
		buf, err = getVectorBuffer(
			e.ParticleCatalog(snaps[0], 0),
			gConfig.SnapshotType, gConfig.Endianness,
		)
		if err != nil {
			return nil, err
		}

		ids, err = convertSortedIDs(rawIds, int(config.snap), vars, buf, e)
		if err != nil {
			return nil, err
		}

	default:
		panic("Impossible")
	}

	// Tag subhalos, if neccessary.
	exclude := make([]bool, len(ids))
	switch config.exclusionStrategy {
	case "none":
	case "subhalo":
		panic("subhalo is not implemented")
	case "overlap":
		var err error
		exclude, err = findOverlapSubs(ids, snaps, vars, buf, e, config)
		if err != nil {
			return nil, err
		}
	}

	// Generate lines
	intCols := [][]int{ids, snaps}
	floatCols := [][]float64{}
	colOrder := []int{0, 1}
	lines := catalog.FormatCols(intCols, floatCols, colOrder)

	// Filter
	fLines := []string{}
	for i := range lines {
		if !exclude[i] {
			fLines = append(fLines, lines[i])
		}
	}

	// Multiply
	mLines := []string{}
	for i := range fLines {
		for j := 0; j < int(config.mult); j++ {
			mLines = append(mLines, fLines[i])
		}
	}

	cString := catalog.CommentString(
		[]string{"ID", "Snapshot"}, []string{}, []int{0, 1}, []int{1, 1},
	)
	mLines = append([]string{cString}, mLines...)

	if logging.Mode == logging.Performance {
		log.Printf("Time: %s", time.Since(t).String())
		log.Printf("Memory:\n%s", logging.MemString())
	}

	return mLines, nil
}

func getIDs(idStart, idEnd int64, ids []int64) []int {
	if idStart != -1 {
		out := make([]int, idEnd-idStart)
		for i := range out {
			out[i] = int(idStart) + i
		}
		return out
	} else {
		out := make([]int, len(ids))
		for i := range out {
			out[i] = int(ids[i])
		}
		return out
	}
}

func convertSortedIDs(
	rawIDs []int, snap int, vars *halo.VarColumns,
	buf io.VectorBuffer, e *env.Environment,
) ([]int, error) {
	maxID := 0
	for _, id := range rawIDs {
		if id > maxID {
			maxID = id
		}
	}

	rids, err := memo.ReadSortedRockstarIDs(snap, maxID, vars, buf, e)
	if err != nil {
		return nil, err
	}

	ids := make([]int, len(rawIDs))
	for i := range ids {
		ids[i] = rids[rawIDs[i]]
	}
	return ids, nil
}

func findOverlapSubs(
	rawIDs, snaps []int, vars *halo.VarColumns,
	buf io.VectorBuffer, e *env.Environment, config *IDConfig,
) ([]bool, error) {
	isSub := make([]bool, len(rawIDs))

	// Group by snapshot.
	snapGroups := make(map[int][]int)
	groupIdxs := make(map[int][]int)
	for i, id := range rawIDs {
		snap := snaps[i]
		snapGroups[snap] = append(snapGroups[snap], id)
		groupIdxs[snap] = append(groupIdxs[snap], i)
	}

	// Load each snapshot.
	hds, _, err := memo.ReadHeaders(snaps[0], buf, e)
	if err != nil {
		return nil, err
	}
	hd := hds[0]

	for snap, group := range snapGroups {
		rids, err := memo.ReadSortedRockstarIDs(snap, -1, vars, buf, e)
		if err != nil {
			return nil, err
		}
		_, xs, ys, zs, _, rs, err := memo.ReadRockstar(snap, rids, vars, buf, e)

		g := halo.NewGrid(finderCells, hd.TotalWidth, len(xs))
		g.Insert(xs, ys, zs)
		sf := halo.NewSubhaloFinder(g)
		sf.FindSubhalos(xs, ys, zs, rs, config.exclusionRadiusMult)

		for i, id := range group {
			origIdx := groupIdxs[snap][i]
			// TODO: Holy linear search, batman! Fix this.
			for j, checkID := range rids {
				if checkID == id {
					isSub[origIdx] = sf.HostCount(j) > 0
					break
				} else if j == len(rids)-1 {
					return nil, fmt.Errorf("ID %d not in halo list.", id)
				}
			}
		}
	}
	return isSub, nil
}
<|endoftext|>


<commit_before>package main

import "fmt"
import "log"
import _ "errors"
import "os"

import "encoding/json"
import "io/ioutil"
import "net/http"
import "net/url"

import _ "database/sql"
import _ "github.com/mattn/go-sqlite3"
import "github.com/jmoiron/sqlx"

// `q` is the YQL query
func yql(jrsp interface{}, q string) (err error) {
	// form the YQL URL:
	u := `http://query.yahooapis.com/v1/public/yql?q=` + url.QueryEscape(q) + `&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys`
	resp, err := http.Get(u)
	if err != nil {
		return
	}

	// read body:
	defer resp.Body.Close()

	// Need a 200 response:
	if resp.StatusCode != 200 {
		err = fmt.Errorf("%s", resp.Status)
		return
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	// print JSON to console:
	//fmt.Printf("%s\n\n", body)

	// decode JSON:
	err = json.Unmarshal(body, jrsp)
	return
}

// Head to http://developer.yahoo.com/yql/console/?q=select%20*%20from%20yahoo.finance.quote%20where%20symbol%20in%20(%22YHOO%22%2C%22AAPL%22%2C%22GOOG%22%2C%22MSFT%22)&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys
// to understand this JSON structure.

type Quote struct {
	Symbol             string "symbol"
	LastTradePriceOnly string
}

type QuoteResponse struct {
	Query *struct {
		Results *struct {
			Quote *Quote "quote"
		} "results"
	} "query"
}

type Historical struct {
	Date  string
	Close string
}

type HistoricalResponse struct {
	Query *struct {
		Results *struct {
			Quote []*Historical "quote"
		} "results"
	} "query"
}

func db_create_schema(path string) (db *sqlx.DB, err error) {
	// using sqlite 3.8.0 release
	db, err = sqlx.Connect("sqlite3", path)
	if err != nil {
		db.Close()
		return
	}

	if _, err = db.Exec(`
create table if not exists stock_track (
	symbol TEXT NOT NULL,
	purchase_price TEXT NOT NULL,
	purchase_date TEXT NOT NULL,
	purchaser_email TEXT NOT NULL,
	trailing_stop_percent TEXT NOT NULL
)`); err != nil {
		db.Close()
		return
	}

	if _, err = db.Exec(`
create table if not exists stock_history (
	symbol TEXT NOT NULL,
	date TEXT NOT NULL,
	closing_price TEXT NOT NULL
)`); err != nil {
		db.Close()
		return
	}

	// Add some test data:
	db.Execl(`
insert into stock_track (symbol, purchase_price, purchase_date, purchaser_email, trailing_stop_percent)
            values ('MSFT', '30.00', '2013-12-01', 'email@example.org', '20.00')
`)
	db.Execl(`
insert into stock_history (symbol, date, closing_price)
            values        ('MSFT', '2013-12-20', '36.80')
`)

	return
}

type dbStockPrice struct {
	Symbol              string `db:"symbol"`
	PurchasePrice       string `db:"purchase_price"`
	PurchaseDate        string `db:"purchase_date"`
	PurchaserEmail      string `db:"purchaser_email"`
	TrailingStopPercent string `db:"trailing_stop_percent"`
	LastCloseDate       string `db:"closing_date"`
	LastClosePrice      string `db:"closing_price"`
}

// main:
func main() {
	const dbPath = "./stocks.db"

	// Create our DB file and its schema:
	os.Remove(dbPath)
	db, err := db_create_schema(dbPath)
	if err != nil {
		log.Fatal(err)
		return
	}
	defer db.Close()

	// Query stocks table:
	stocks := make([]dbStockPrice, 0, 4) // make(type, len, capacity)
	if err = db.Select(&stocks, `
select s.symbol, s.purchase_price, s.purchase_date, s.purchaser_email, s.trailing_stop_percent, h.date as closing_date, h.closing_price
from stock_track as s
left join stock_history as h on h.symbol = s.symbol and h.date = (select max(date) from stock_history where symbol = s.symbol)
`); err != nil {
		log.Fatal(err)
	}

	for i, value := range stocks {
		if i == 0 {
			fmt.Printf("%#v", value)
		} else {
			fmt.Printf(", %#v", value)
		}
	}
	fmt.Println()

	// get current price of MSFT:
	quot := new(QuoteResponse)
	err = yql(quot, `select Symbol, LastTradePriceOnly from yahoo.finance.quote where symbol in ("MSFT")`)
	if err != nil {
		log.Fatal(err)
		return
	}

	fmt.Printf("%#v\n", *quot.Query.Results.Quote)

	// get historical data for MSFT:
	hist := new(HistoricalResponse)
	if err = yql(hist, `select Date, Close from yahoo.finance.historicaldata where symbol = "MSFT" and startDate = "2013-12-16" and endDate = "2013-12-20"`); err != nil {
		log.Fatal(err)
		return
	}

	for i, value := range hist.Query.Results.Quote {
		if i == 0 {
			fmt.Printf("%#v", *value)
		} else {
			fmt.Printf(", %#v", *value)
		}
	}
	fmt.Println()
	return
}
<commit_msg>Determining last fetched date per stock.<commit_after>package main

import "fmt"
import "log"
import _ "errors"
import "os"

import "time"
import "encoding/json"
import "io/ioutil"
import "net/http"
import "net/url"

import "database/sql"
import _ "github.com/mattn/go-sqlite3"
import "github.com/jmoiron/sqlx"

// Head to http://developer.yahoo.com/yql/console/?q=select%20*%20from%20yahoo.finance.quote%20where%20symbol%20in%20(%22YHOO%22%2C%22AAPL%22%2C%22GOOG%22%2C%22MSFT%22)&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys
// to understand this JSON structure.

type Quote struct {
	Symbol             string `json:"Symbol"`
	LastTradePriceOnly string
}

type QuoteResponse struct {
	Query struct {
		CreatedDate string `json:"created"`
		Results     *struct {
			Quote Quote `json:"quote"`
		} `json:"results"`
	} `json:"query"`
}

type Historical struct {
	Date  string
	Close string
}

type HistoricalResponse struct {
	Query struct {
		CreatedDate string `json:"created"`
		Results     *struct {
			Quote []Historical `json:"quote"`
		} `json:"results"`
	} `json:"query"`
}

// `q` is the YQL query
func yql(jrsp interface{}, q string) (err error) {
	// form the YQL URL:
	u := `http://query.yahooapis.com/v1/public/yql?q=` + url.QueryEscape(q) + `&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys`
	resp, err := http.Get(u)
	if err != nil {
		return
	}

	// read body:
	defer resp.Body.Close()

	// Need a 200 response:
	if resp.StatusCode != 200 {
		err = fmt.Errorf("%s", resp.Status)
		return
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	// print JSON to console:
	//fmt.Printf("%s\n\n", body)

	// decode JSON:
	err = json.Unmarshal(body, jrsp)
	return
}

func db_create_schema(path string) (db *sqlx.DB, err error) {
	// using sqlite 3.8.0 release
	db, err = sqlx.Connect("sqlite3", path)
	if err != nil {
		db.Close()
		return
	}

	if _, err = db.Exec(`
create table if not exists stock_track (
	symbol TEXT NOT NULL,
	purchase_price TEXT NOT NULL,
	purchase_date TEXT NOT NULL,
	purchaser_email TEXT NOT NULL,
	trailing_stop_percent TEXT NOT NULL
)`); err != nil {
		db.Close()
		return
	}

	if _, err = db.Exec(`
create table if not exists stock_history (
	symbol TEXT NOT NULL,
	closing_date TEXT NOT NULL,
	closing_price TEXT NOT NULL
)`); err != nil {
		db.Close()
		return
	}

	// Add some test data:
	db.Execl(`
insert into stock_track (symbol, purchase_price, purchase_date, purchaser_email, trailing_stop_percent)
            values ('MSFT', '30.00', '2013-12-01', 'email@example.org', '20.00')
`)

	return
}

type dbStock struct {
	Symbol              string `db:"symbol"`
	PurchasePrice       string `db:"purchase_price"`
	PurchaseDate        string `db:"purchase_date"`
	PurchaserEmail      string `db:"purchaser_email"`
	TrailingStopPercent string `db:"trailing_stop_percent"`
}

type dbStockHistory struct {
	Symbol       string `db:"symbol"`
	ClosingDate  string `db:"closing_date"`
	ClosingPrice string `db:"closing_price"`
}

// remove the time component of a datetime to get just a date at 00:00:00
func toDate(t time.Time) time.Time {
	hour, min, sec := t.Clock()
	nano := t.Nanosecond()

	d := time.Duration(-(uint64(nano) + uint64(sec)*uint64(time.Second) + uint64(min)*uint64(time.Minute) + uint64(hour)*uint64(time.Hour)))
	return t.Add(d)
}

// main:
func main() {
	const dbPath = "./stocks.db"

	// Get the New York location for stock timezone:
	nyLoc, _ := time.LoadLocation("America/New_York")

	// Create our DB file and its schema:
	os.Remove(dbPath)
	db, err := db_create_schema(dbPath)
	if err != nil {
		log.Fatal(err)
		return
	}
	defer db.Close()

	// Query stocks table:
	stocks := make([]dbStock, 0, 4) // make(type, len, capacity)
	if err = db.Select(&stocks, `
select s.symbol, s.purchase_price, s.purchase_date, s.purchaser_email, s.trailing_stop_percent
from stock_track as s
`); err != nil {
		log.Fatal(err)
	}

	fmt.Print("Stocks:\n")
	for _, st := range stocks {
		fmt.Printf("  %#v\n", st)

		// Determine the last-fetched date for the stock:
		rec := new(dbStockHistory)
		err = db.Get(rec, `
select h.symbol, h.closing_date, h.closing_price
from stock_history as h
where date(h.closing_date) = (select max(date(closing_date)) from stock_history where symbol = h.symbol)
  and h.symbol = ?
`, st.Symbol)

		var lastDate time.Time
		if err == sql.ErrNoRows {
			// No row; use 7 days ago in NYC time:
			lastDate = toDate(time.Now().Add(time.Duration(time.Hour * 24 * -7)).In(nyLoc))
		} else if err != nil {
			log.Fatal(err)
		} else {
			// Extract the last-fetched date from the db record in NYC time:
			lastDate, _ = time.ParseInLocation(time.RFC3339, rec.ClosingDate, nyLoc)
			lastDate = toDate(lastDate)
		}
		fmt.Println(lastDate.Format("2006-01-02 15:04:05 -0700"))

	}
	fmt.Println()

	//// get historical data for MSFT:
	//hist := new(HistoricalResponse)
	//if err = yql(hist, `select Date, Close from yahoo.finance.historicaldata where symbol = "MSFT" and startDate = "2013-12-16" and endDate = "2013-12-20"`); err != nil {
	//	log.Fatal(err)
	//	return
	//}

	//for i, value := range hist.Query.Results.Quote {
	//	if i == 0 {
	//		fmt.Printf("%#v", value)
	//	} else {
	//		fmt.Printf(", %#v", value)
	//	}
	//}
	//fmt.Println()

	//// get current price of MSFT:
	//quot := new(QuoteResponse)
	//err = yql(quot, `select Symbol, LastTradePriceOnly from yahoo.finance.quote where symbol in ("MSFT")`)
	//if err != nil {
	//	log.Fatal(err)
	//	return
	//}

	//fmt.Printf("%#v\n", quot.Query)

	return
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"gopkg.in/alecthomas/kingpin.v2"

	"github.com/ark-lang/ark/src/codegen"
	"github.com/ark-lang/ark/src/codegen/LLVMCodegen"
	"github.com/ark-lang/ark/src/doc"
	"github.com/ark-lang/ark/src/lexer"
	"github.com/ark-lang/ark/src/parser"
	"github.com/ark-lang/ark/src/semantic"
	"github.com/ark-lang/ark/src/util"
	"github.com/ark-lang/ark/src/util/log"
)

const (
	VERSION = "0.0.2"
	AUTHOR  = "The Ark Authors"
)

var startTime time.Time

func main() {
	startTime = time.Now()

	command := kingpin.MustParse(app.Parse(os.Args[1:]))
	log.SetLevel(*logLevel)
	log.SetTags(*logTags)

	switch command {
	case buildCom.FullCommand():
		if len(*buildInputs) == 0 {
			setupErr("No input files passed.")
		}

		// build the files
		outputType := parseOutputType(*buildOutputType)
		build(*buildInputs, *buildOutput, *buildCodegen, outputType, *buildOptLevel)

		printFinishedMessage(startTime, buildCom.FullCommand(), len(*buildInputs))

	case docgenCom.FullCommand():
		docgen(*docgenInputs, *docgenDir)
		printFinishedMessage(startTime, docgenCom.FullCommand(), len(*docgenInputs))
	}
}

func printFinishedMessage(startTime time.Time, command string, numFiles int) {
	dur := time.Since(startTime)
	log.Info("main", "%s (%d file(s), %.2fms)\n",
		util.TEXT_GREEN+util.TEXT_BOLD+fmt.Sprintf("Finished %s", command)+util.TEXT_RESET,
		numFiles, float32(dur.Nanoseconds())/1000000)
}

func setupErr(err string, stuff ...interface{}) {
	log.Error("main", util.TEXT_RED+util.TEXT_BOLD+"error:"+util.TEXT_RESET+" %s\n",
		fmt.Sprintf(err, stuff...))
	os.Exit(util.EXIT_FAILURE_SETUP)
}

func parseFiles(inputs []string) ([]*parser.Module, *parser.ModuleLookup) {
	if len(inputs) != 1 {
		setupErr("Please specify only one file or module to build")
	}

	var modulesToRead []*parser.ModuleName
	var modules []*parser.Module
	moduleLookup := parser.NewModuleLookup("")
	depGraph := parser.NewDependencyGraph()

	input := inputs[0]
	if strings.HasSuffix(input, ".ark") {
		// Handle the special case of a single .ark file
		modname := &parser.ModuleName{Parts: []string{"__main"}}
		module := &parser.Module{
			Name:    modname,
			Dirpath: "",
		}
		moduleLookup.Create(modname).Module = module

		// Read
		sourcefile, err := lexer.NewSourcefile(input)
		if err != nil {
			setupErr("%s", err.Error())
		}

		// Lex
		sourcefile.Tokens = lexer.Lex(sourcefile)

		// Parse
		parsedFile, deps := parser.Parse(sourcefile)
		module.Trees = append(module.Trees, parsedFile)

		// Add dependencies to parse array
		for _, dep := range deps {
			depname := parser.NewModuleName(dep)
			modulesToRead = append(modulesToRead, depname)
			depGraph.AddDependency(modname, depname)
		}

		modules = append(modules, module)
	} else {
		if strings.ContainsAny(input, `\/. `) {
			setupErr("Invalid module name: %s", input)
		}

		modname := &parser.ModuleName{Parts: strings.Split(input, "::")}
		modulesToRead = append(modulesToRead, modname)
	}

	moduleSearchpaths := append(*buildSearchpaths, *buildBasedir)

	log.Timed("read/lex/parse phase", "", func() {
		for i := 0; i < len(modulesToRead); i++ {
			modname := modulesToRead[i]

			fi, dirpath := findModuleDir(moduleSearchpaths, modname.ToPath())
			if fi == nil {
				setupErr("Couldn't find module `%s`", modname)
			}

			if !fi.IsDir() {
				setupErr("Expected path `%s` to be directory, was file.", dirpath)
			}

			module := &parser.Module{
				Name:    modname,
				Dirpath: dirpath,
			}
			moduleLookup.Create(modname).Module = module

			// Check module children
			childFiles, err := ioutil.ReadDir(dirpath)
			if err != nil {
				setupErr("%s", err.Error())
			}

			for _, childFile := range childFiles {
				if strings.HasPrefix(childFile.Name(), ".") || !strings.HasSuffix(childFile.Name(), ".ark") {
					continue
				}

				actualFile := filepath.Join(dirpath, childFile.Name())

				// Read
				sourcefile, err := lexer.NewSourcefile(actualFile)
				if err != nil {
					setupErr("%s", err.Error())
				}

				// Lex
				sourcefile.Tokens = lexer.Lex(sourcefile)

				// Parse
				parsedFile, deps := parser.Parse(sourcefile)
				module.Trees = append(module.Trees, parsedFile)

				// Add dependencies to parse array
				for _, dep := range deps {
					depname := parser.NewModuleName(dep)
					modulesToRead = append(modulesToRead, depname)
					depGraph.AddDependency(modname, depname)
				}
			}

			// this just prevents the same module being added to the module list twice.
			// the same module will still be parsed more than once if used more than once.
			// TODO parse each module only one time, even if used more than once.
			alreadyUsed := false
			for _, m := range modules {
				if m.Name.String() == module.Name.String() {
					alreadyUsed = true
					break
				}
			}

			if !alreadyUsed {
				modules = append(modules, module)
			}
		}
	})

	// Check for cyclic dependencies (in modules)
	log.Timed("cyclic dependency check", "", func() {
		errs := depGraph.DetectCycles()
		if len(errs) > 0 {
			log.Errorln("main", "error: Encountered cyclic dependecies:")
			for _, cycle := range errs {
				log.Errorln("main", "%s", cycle)
			}
			os.Exit(util.EXIT_FAILURE_SETUP)
		}
	})

	hasMainFunc := false
	// construction
	log.Timed("construction phase", "", func() {
		for _, module := range modules {
			parser.Construct(module, moduleLookup)

			// Use module scope to check for main function
			mainIdent := module.ModScope.GetIdent(parser.UnresolvedName{Name: "main"})
			if mainIdent != nil && mainIdent.Type == parser.IDENT_FUNCTION {
				hasMainFunc = true
			}
		}
	})

	// and here we check if we should
	// bother continuing any further...
	if !hasMainFunc {
		log.Error("main", util.Red("error: ")+"main function not found\n")
		os.Exit(1)
	}

	return modules, moduleLookup
}

func findModuleDir(searchPaths []string, modulePath string) (fi os.FileInfo, path string) {
	for _, searchPath := range searchPaths {
		path := filepath.Join(searchPath, modulePath)
		fi, err := os.Stat(path)
		if err == nil {
			return fi, path
		}

		if !os.IsNotExist(err) {
			setupErr("%s\n", err)
		}
	}

	return nil, ""
}

func build(files []string, outputFile string, cg string, outputType LLVMCodegen.OutputType, optLevel int) {
	constructedModules, moduleLookup := parseFiles(files)

	// resolve
	log.Timed("resolve phase", "", func() {
		for _, module := range constructedModules {
			for _, submod := range module.Parts {
				res := &parser.Resolver{Submodule: submod, ModuleLookup: moduleLookup}
				vis := parser.NewASTVisitor(res)
				vis.VisitSubmodule(submod)
			}
		}
	})

	// type inference
	log.Timed("inference phase", "", func() {
		for _, module := range constructedModules {
			for _, submod := range module.Parts {
				inf := &parser.TypeInferer{Submodule: submod}
				inf.Infer()

				// Dump AST
				log.Debugln("main", "AST of submodule `%s/%s`:", module.Name, submod.File.Name)
				for _, node := range submod.Nodes {
					log.Debugln("main", "%s", node.String())
				}
				log.Debugln("main", "")
			}
		}
	})

	// semantic analysis
	log.Timed("semantic analysis phase", "", func() {
		for _, module := range constructedModules {
			for _, submod := range module.Parts {
				sem := semantic.NewSemanticAnalyzer(submod, *buildOwnership, *ignoreUnused)
				vis := parser.NewASTVisitor(sem)
				vis.VisitSubmodule(submod)
				sem.Finalize()
			}
		}
	})

	// codegen
	if cg != "none" {
		var gen codegen.Codegen

		switch cg {
		case "llvm":
			gen = &LLVMCodegen.Codegen{
				OutputName: outputFile,
				OutputType: outputType,
				OptLevel:   optLevel,
			}
		default:
			log.Error("main", util.Red("error: ")+"Invalid backend choice `"+cg+"`")
			os.Exit(1)
		}

		log.Timed("codegen phase", "", func() {
			gen.Generate(constructedModules)
		})
	}

}

func docgen(input []string, dir string) {
	constructedModules, _ := parseFiles(input)

	gen := &doc.Docgen{
		Input: constructedModules,
		Dir:   dir,
	}

	gen.Generate()
}
<commit_msg>Redo the already parsed check.<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"gopkg.in/alecthomas/kingpin.v2"

	"github.com/ark-lang/ark/src/codegen"
	"github.com/ark-lang/ark/src/codegen/LLVMCodegen"
	"github.com/ark-lang/ark/src/doc"
	"github.com/ark-lang/ark/src/lexer"
	"github.com/ark-lang/ark/src/parser"
	"github.com/ark-lang/ark/src/semantic"
	"github.com/ark-lang/ark/src/util"
	"github.com/ark-lang/ark/src/util/log"
)

const (
	VERSION = "0.0.2"
	AUTHOR  = "The Ark Authors"
)

var startTime time.Time

func main() {
	startTime = time.Now()

	command := kingpin.MustParse(app.Parse(os.Args[1:]))
	log.SetLevel(*logLevel)
	log.SetTags(*logTags)

	switch command {
	case buildCom.FullCommand():
		if len(*buildInputs) == 0 {
			setupErr("No input files passed.")
		}

		// build the files
		outputType := parseOutputType(*buildOutputType)
		build(*buildInputs, *buildOutput, *buildCodegen, outputType, *buildOptLevel)

		printFinishedMessage(startTime, buildCom.FullCommand(), len(*buildInputs))

	case docgenCom.FullCommand():
		docgen(*docgenInputs, *docgenDir)
		printFinishedMessage(startTime, docgenCom.FullCommand(), len(*docgenInputs))
	}
}

func printFinishedMessage(startTime time.Time, command string, numFiles int) {
	dur := time.Since(startTime)
	log.Info("main", "%s (%d file(s), %.2fms)\n",
		util.TEXT_GREEN+util.TEXT_BOLD+fmt.Sprintf("Finished %s", command)+util.TEXT_RESET,
		numFiles, float32(dur.Nanoseconds())/1000000)
}

func setupErr(err string, stuff ...interface{}) {
	log.Error("main", util.TEXT_RED+util.TEXT_BOLD+"error:"+util.TEXT_RESET+" %s\n",
		fmt.Sprintf(err, stuff...))
	os.Exit(util.EXIT_FAILURE_SETUP)
}

func parseFiles(inputs []string) ([]*parser.Module, *parser.ModuleLookup) {
	if len(inputs) != 1 {
		setupErr("Please specify only one file or module to build")
	}

	var modulesToRead []*parser.ModuleName
	var modules []*parser.Module
	moduleLookup := parser.NewModuleLookup("")
	depGraph := parser.NewDependencyGraph()

	input := inputs[0]
	if strings.HasSuffix(input, ".ark") {
		// Handle the special case of a single .ark file
		modname := &parser.ModuleName{Parts: []string{"__main"}}
		module := &parser.Module{
			Name:    modname,
			Dirpath: "",
		}
		moduleLookup.Create(modname).Module = module

		// Read
		sourcefile, err := lexer.NewSourcefile(input)
		if err != nil {
			setupErr("%s", err.Error())
		}

		// Lex
		sourcefile.Tokens = lexer.Lex(sourcefile)

		// Parse
		parsedFile, deps := parser.Parse(sourcefile)
		module.Trees = append(module.Trees, parsedFile)

		// Add dependencies to parse array
		for _, dep := range deps {
			depname := parser.NewModuleName(dep)
			modulesToRead = append(modulesToRead, depname)
			depGraph.AddDependency(modname, depname)
		}

		modules = append(modules, module)
	} else {
		if strings.ContainsAny(input, `\/. `) {
			setupErr("Invalid module name: %s", input)
		}

		modname := &parser.ModuleName{Parts: strings.Split(input, "::")}
		modulesToRead = append(modulesToRead, modname)
	}

	moduleSearchpaths := append(*buildSearchpaths, *buildBasedir)

	log.Timed("read/lex/parse phase", "", func() {
		for i := 0; i < len(modulesToRead); i++ {
			modname := modulesToRead[i]

			// Skip already loaded modules
			if _, err := moduleLookup.Get(modname); err == nil {
				continue
			}

			fi, dirpath := findModuleDir(moduleSearchpaths, modname.ToPath())
			if fi == nil {
				setupErr("Couldn't find module `%s`", modname)
			}

			if !fi.IsDir() {
				setupErr("Expected path `%s` to be directory, was file.", dirpath)
			}

			module := &parser.Module{
				Name:    modname,
				Dirpath: dirpath,
			}
			moduleLookup.Create(modname).Module = module

			// Check module children
			childFiles, err := ioutil.ReadDir(dirpath)
			if err != nil {
				setupErr("%s", err.Error())
			}

			for _, childFile := range childFiles {
				if strings.HasPrefix(childFile.Name(), ".") || !strings.HasSuffix(childFile.Name(), ".ark") {
					continue
				}

				actualFile := filepath.Join(dirpath, childFile.Name())

				// Read
				sourcefile, err := lexer.NewSourcefile(actualFile)
				if err != nil {
					setupErr("%s", err.Error())
				}

				// Lex
				sourcefile.Tokens = lexer.Lex(sourcefile)

				// Parse
				parsedFile, deps := parser.Parse(sourcefile)
				module.Trees = append(module.Trees, parsedFile)

				// Add dependencies to parse array
				for _, dep := range deps {
					depname := parser.NewModuleName(dep)
					modulesToRead = append(modulesToRead, depname)
					depGraph.AddDependency(modname, depname)
				}
			}

			modules = append(modules, module)
		}
	})

	// Check for cyclic dependencies (in modules)
	log.Timed("cyclic dependency check", "", func() {
		errs := depGraph.DetectCycles()
		if len(errs) > 0 {
			log.Errorln("main", "error: Encountered cyclic dependecies:")
			for _, cycle := range errs {
				log.Errorln("main", "%s", cycle)
			}
			os.Exit(util.EXIT_FAILURE_SETUP)
		}
	})

	hasMainFunc := false
	// construction
	log.Timed("construction phase", "", func() {
		for _, module := range modules {
			parser.Construct(module, moduleLookup)

			// Use module scope to check for main function
			mainIdent := module.ModScope.GetIdent(parser.UnresolvedName{Name: "main"})
			if mainIdent != nil && mainIdent.Type == parser.IDENT_FUNCTION {
				hasMainFunc = true
			}
		}
	})

	// and here we check if we should
	// bother continuing any further...
	if !hasMainFunc {
		log.Error("main", util.Red("error: ")+"main function not found\n")
		os.Exit(1)
	}

	return modules, moduleLookup
}

func findModuleDir(searchPaths []string, modulePath string) (fi os.FileInfo, path string) {
	for _, searchPath := range searchPaths {
		path := filepath.Join(searchPath, modulePath)
		fi, err := os.Stat(path)
		if err == nil {
			return fi, path
		}

		if !os.IsNotExist(err) {
			setupErr("%s\n", err)
		}
	}

	return nil, ""
}

func build(files []string, outputFile string, cg string, outputType LLVMCodegen.OutputType, optLevel int) {
	constructedModules, moduleLookup := parseFiles(files)

	// resolve
	log.Timed("resolve phase", "", func() {
		for _, module := range constructedModules {
			for _, submod := range module.Parts {
				res := &parser.Resolver{Submodule: submod, ModuleLookup: moduleLookup}
				vis := parser.NewASTVisitor(res)
				vis.VisitSubmodule(submod)
			}
		}
	})

	// type inference
	log.Timed("inference phase", "", func() {
		for _, module := range constructedModules {
			for _, submod := range module.Parts {
				inf := &parser.TypeInferer{Submodule: submod}
				inf.Infer()

				// Dump AST
				log.Debugln("main", "AST of submodule `%s/%s`:", module.Name, submod.File.Name)
				for _, node := range submod.Nodes {
					log.Debugln("main", "%s", node.String())
				}
				log.Debugln("main", "")
			}
		}
	})

	// semantic analysis
	log.Timed("semantic analysis phase", "", func() {
		for _, module := range constructedModules {
			for _, submod := range module.Parts {
				sem := semantic.NewSemanticAnalyzer(submod, *buildOwnership, *ignoreUnused)
				vis := parser.NewASTVisitor(sem)
				vis.VisitSubmodule(submod)
				sem.Finalize()
			}
		}
	})

	// codegen
	if cg != "none" {
		var gen codegen.Codegen

		switch cg {
		case "llvm":
			gen = &LLVMCodegen.Codegen{
				OutputName: outputFile,
				OutputType: outputType,
				OptLevel:   optLevel,
			}
		default:
			log.Error("main", util.Red("error: ")+"Invalid backend choice `"+cg+"`")
			os.Exit(1)
		}

		log.Timed("codegen phase", "", func() {
			gen.Generate(constructedModules)
		})
	}

}

func docgen(input []string, dir string) {
	constructedModules, _ := parseFiles(input)

	gen := &doc.Docgen{
		Input: constructedModules,
		Dir:   dir,
	}

	gen.Generate()
}
<|endoftext|>


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

package di

import (
	"testing"
	"reflect"
)

type Bar interface {
	test(int) string
}

type Foo struct {
	a string
}

func (f *Foo) test(b int) string {
	return f.a
}

type Writer interface {
	Write(string) string
}

type ResponseWriter struct {
	Foo *Foo `inject`
	t   int
}

func (*ResponseWriter) Write(s string) string {
	return s
}

type Request struct {
	Bar Bar `inject`
}

type Context struct {
	data string
}

type Controller struct {
	*Context `inject`
	Response  Writer `inject`
	response  Writer `inject`
	Response2 Writer `inject:"xyz"`
	Response3 Writer

	Request   Request `inject`
	action    string
}

func TestRegister(t *testing.T) {
	var foo1 Foo
	var foo2 Foo = Foo{}
	var foo3 *Foo = &Foo{}
	var foo4 []Foo = make([]Foo, 0)
	var foo5 [10]Foo
	var foo6 chan Foo
	var foo7 map[string]Foo
	var bar1 Bar = &Foo{}
	var bar2 *Bar = (*Bar)(nil)

	tests := []struct {
		value  interface{}
		result reflect.Type
	}{
		{100, reflect.TypeOf(int(0))},
		{"abc", reflect.TypeOf(string(""))},
		{true, reflect.TypeOf(bool(false))},
		{100, reflect.TypeOf(int(0))},
		{foo1, reflect.TypeOf(Foo{})},
		{foo2, reflect.TypeOf(Foo{})},
		{foo3, reflect.TypeOf(&Foo{})},
		{foo4, reflect.TypeOf(make([]Foo, 0))},
		{foo5, reflect.TypeOf([10]Foo{})},
		{foo6, reflect.TypeOf(make(chan Foo, 0))},
		{foo7, reflect.TypeOf(make(map[string]Foo))},
		{bar1, reflect.TypeOf(&Foo{})},
		{bar2, reflect.TypeOf((*Bar)(nil))},
	}

	for _, test := range tests {
		c := NewContainer()
		c.Register(test.value)
		if !c.HasRegistered(test.result) {
			t.Errorf("Register(%v) failed, expected %v is registered", test.value, test.result)
		}
	}
}

func TestRegisterInterface(t *testing.T) {
	var foo Foo
	var bar *Bar = (*Bar)(nil)

	c := NewContainer()
	barType := InterfaceOf(bar)
	c.RegisterAs(&foo, barType)
	if !c.HasRegistered(barType) {
		t.Errorf("RegisterAs(%v, %v) failed, expected %v is registered", &foo, barType, barType)
	}

	fooType := reflect.TypeOf(&foo)
	if c.HasRegistered(fooType) {
		t.Errorf("RegisterAs(%v, %v) failed, expected %v is NOT registered", &foo, barType, fooType)
	}
}

func TestRegisterProvider(t *testing.T) {
	var f = func(Container) reflect.Value {
		return reflect.ValueOf(Foo{})
	}
	var bar *Bar = (*Bar)(nil)
	funcType := reflect.TypeOf(f)
	barType := InterfaceOf(bar)

	c := NewContainer()
	c.RegisterProvider(f, barType, true)
	if !c.HasRegistered(barType) {
		t.Errorf("RegisterProvider(%v, %v) failed, expected %v is registered", f, barType, barType)
	}
	if c.HasRegistered(funcType) {
		t.Errorf("RegisterProvider(%v, %v) failed, expected %v is NOT registered", f, barType, funcType)
	}
}

func TestBuildBasic(t *testing.T) {
	tests := []struct {
		rt     reflect.Type
		result interface{}
	}{
		{reflect.TypeOf(int(0)), 0},
		{reflect.TypeOf(string("abc")), ""},
		{reflect.TypeOf(Foo{}), Foo{}},
	}

	c := NewContainer()
	for _, test := range tests {
		v := c.Make(test.rt)
		if v != test.result {
			t.Errorf("Make(%v): expected %#v, got %#v", test.rt, test.result, v)
		}
	}

	v := c.Make(reflect.TypeOf(&Foo{}))
	if reflect.ValueOf(v).IsNil() {
		t.Errorf("Make(%v) = nil, expected not nil", &Foo{})
	}
}

func TestBuildComplex(t *testing.T) {
	c := NewContainer()

	var slice []int = c.Make(reflect.TypeOf([]int(nil))).([]int)
	if slice == nil || len(slice) != 0 {
		t.Errorf("Make([]int): expected []int{}, got %#v", slice)
	}

	var m map[string]Foo = c.Make(reflect.TypeOf(map[string]Foo{})).(map[string]Foo)
	if m == nil || len(m) != 0 {
		t.Errorf("Make(map[string]Foo): expected map[string]Foo{}, got %#v", slice)
	}

	var ch chan Foo = c.Make(reflect.TypeOf(make(chan Foo))).(chan Foo)
	if ch == nil || len(ch) != 0 {
		t.Errorf("Make(chan Foo): expected chan Foo{}, got %#v", ch)
	}
}

func TestBuildStruct(t *testing.T) {
	c := NewContainer()
	foo := Foo{"abc"}
	c.Register(foo)

	foo1 := c.Make(reflect.TypeOf(Foo{})).(Foo)
	if foo1.a != "abc" {
		t.Errorf("Make(Foo): expected Foo.a=%q, got %q", "abc", foo1.a)
	}

	// the registered version should remain unchanged (registering by value)
	foo1.a = "test"
	foo2 := c.Make(reflect.TypeOf(Foo{})).(Foo)
	if foo2.a != "abc" {
		t.Errorf("Make(Foo) after change: expected Foo.a=%q, got %q", "abc", foo2.a)
	}

	// registering by value doesn't mean registering the corresponding pointer
	foo3 := c.Make(reflect.TypeOf(&Foo{})).(*Foo)
	if foo3 != nil {
		t.Errorf("Make(Foo*) != nil, expected nil")
	}
}

func TestBuildStructPtr(t *testing.T) {
	c := NewContainer()
	foo := &Foo{"abc"}
	c.Register(foo)

	foo1 := c.Make(reflect.TypeOf(&Foo{})).(*Foo)
	if foo1.a != "abc" {
		t.Errorf("Make(*Foo): expected Foo.a=%q, got %q", "abc", foo1.a)
	}

	// the registered version should be unchanged (registering by pointer)
	foo1.a = "test"
	foo2 := c.Make(reflect.TypeOf(&Foo{})).(*Foo)
	if foo2.a != "test" {
		t.Errorf("Make(*Foo) after change: expected Foo.a=%q, got %q", "test", foo2.a)
	}

	// registering by value doesn't mean registering the corresponding pointer
	foo3 := c.Make(reflect.TypeOf(Foo{})).(Foo)
	if foo3.a != "test" {
		t.Errorf("Make(Foo).a = %q, expected %q", foo3.a, "abc")
	}
}

func TestBuildInterface(t *testing.T) {
	c := NewContainer()
	w := ResponseWriter{t: 1}
	writer := InterfaceOf((*Writer)(nil))
	c.RegisterAs(&w, writer)

	// make interface
	r := c.Make(writer).(*ResponseWriter)
	if r.t != 1 {
		t.Errorf("Make(Writer).t = %v, expected 1", r.t)
	}

	// make pointer
	r2 := c.Make(reflect.TypeOf(&w)).(*ResponseWriter)
	if r2 == nil {
		t.Errorf("Make(*ResponseWriter) = nil, expected not nil")
	}

	// making an unregistered concrete struct should result in zero struct
	r3 := c.Make(reflect.TypeOf(w)).(ResponseWriter)
	if r3.t != 0 {
		t.Errorf("Make(ResponseWriter).t = %v, expected 0", r3.t)
	}

	// making an unregistered interface should result in nil
	r4 := c.Make(InterfaceOf((*Bar)(nil)))
	if r4 != nil {
		t.Errorf("Make(Bar) = %#v, expected nil", r4)
	}
}

func TestBuildProvider(t *testing.T) {
	// shared
	c := NewContainer()
	c.RegisterProvider(func(Container) reflect.Value {
		return reflect.ValueOf(&Foo{"abc"})
	}, reflect.TypeOf(&Foo{}), true)
	foo1 := c.Make(reflect.TypeOf(&Foo{})).(*Foo)
	if foo1.a != "abc" {
		t.Errorf("Make(*Foo).a = %q, expected %q", foo1.a, "abc")
	}
	foo1.a = "xyz"
	foo2 := c.Make(reflect.TypeOf(&Foo{})).(*Foo)
	if foo1.a != foo2.a {
		t.Errorf("Changed shared Foo, Make(*Foo).a = %q, expected %q", foo2.a, foo1.a)
	}

	// not shared
	c = NewContainer()
	c.RegisterProvider(func(Container) reflect.Value {
		return reflect.ValueOf(&Foo{"abc"})
	}, reflect.TypeOf(&Foo{}), false)
	foo3 := c.Make(reflect.TypeOf(&Foo{})).(*Foo)
	if foo3.a != "abc" {
		t.Errorf("Make(*Foo).a = %q, expected %q", foo3.a, "abc")
	}
	foo3.a = "xyz"
	foo4 := c.Make(reflect.TypeOf(&Foo{})).(*Foo)
	if foo3.a == foo4.a {
		t.Errorf("Changed non-shared Foo, Make(*Foo).a = %q, expected %q", foo4.a, "abc")
	}
}

func TestBuildInject(t *testing.T) {
	barType := InterfaceOf((*Bar)(nil))
	writerType := InterfaceOf((*Writer)(nil))

	c := NewContainer()
	c.Register(&Context{"abc"})
	c.RegisterAs(&Foo{"123"}, barType)
	c.RegisterAs(reflect.TypeOf(&ResponseWriter{}), writerType)

	con := c.Make(reflect.TypeOf(Controller{})).(Controller)
	if con.action != "" {
		t.Errorf("Controller.action = %q, expected %q", con.action, "")
	}
	if con.data != "abc" {
		t.Errorf("Controller.data = %q, expected %q", con.data, "abc")
	}
	if con.Request.Bar.test(0) != "123" {
		t.Errorf("Controller.request.bar.test() = %q, expected %q", con.Request.Bar.test(0), "123")
	}
}

func TestInject(t *testing.T) {
	writerType := InterfaceOf((*Writer)(nil))

	c := NewContainer()
	c.Register(&Context{"abc"})
	c.RegisterAs(&ResponseWriter{t: 123}, writerType)
	c.RegisterAs(&Foo{"xyz"}, InterfaceOf((*Bar)(nil)))
	con := &Controller{}
	c.Inject(con)
	// anonymous field injection
	if con.data != "abc" {
		t.Errorf("Controller.data = %q, expected %q", con.data, "abc")
	}
	// valid injection
	if con.Response == nil {
		t.Errorf("Controller.Response should not be nil")
	}
	if con.Response.(*ResponseWriter).t != 123 {
		t.Errorf("Controller.Response.t = %q, expected %q", con.Response.(*ResponseWriter).t, 123)
	}
	// inject struct tag
	if con.Response2 == nil {
		t.Errorf("Controller.Response2 should not be nil")
	}
	// field not marked with "inject" should not be injected
	if con.Response3 != nil {
		t.Errorf("Controller.Response3 should be nil")
	}
	// unexported should not be injected
	if con.response != nil {
		t.Errorf("Controller.response should be nil")
	}

	// recursive injection
	if con.Request.Bar == nil {
		t.Errorf("Controller.Request.bar = nil, expected not nil")
	}
	if con.Request.Bar.(*Foo).a != "xyz" {
		t.Errorf("Controller.Request.bar.(*Foo).a = %q, expected %q", con.Request.Bar.(*Foo).a, "xyz")
	}
}

func TestCall(t *testing.T) {
	writerType := InterfaceOf((*Writer)(nil))

	c := NewContainer()
	w := ResponseWriter{t: 1}
	context := Context{"abc"}
	c.RegisterAs(&w, writerType)
	c.Register(&context)

	f := func(cc *Context, w Writer, s string) (string, string, string) {
		return cc.data, w.Write("test"), s
	}
	result := c.Call(f)
	if len(result) != 3 {
		t.Error("The return result should contain 3 values.")
	}
	if result[0] != "abc" || result[1] != "test" {
		t.Errorf("The return result was (%q, %q, %q), expected (%q, %q, %q)", result[0], result[1], result[2], "abc", "test", "")
	}
}

func TestParent(t *testing.T) {
	c := NewContainer()
	if c.ParentContainer() != nil {
		t.Error("ParentContainer() != nil, expected nil")
	}

	f := c.Make(reflect.TypeOf(Foo{})).(Foo)
	if f.a != "" {
		t.Error("Build(Foo) returns non-zero Foo struct, expected zero")
	}

	p := NewContainer()
	p.Register(Foo{"abc"})
	c.SetParentContainer(p)
	if c.ParentContainer() == nil {
		t.Error("ParentContainer() == nil, expected not nil")
	}
	f = c.Make(reflect.TypeOf(Foo{})).(Foo)
	if f.a != "abc" {
		t.Errorf("Build(Foo).a=%q, expected %q", f.a, "abc")
	}
}
<commit_msg>add more test coverage<commit_after>// Copyright 2015 Qiang Xue. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package di

import (
	"testing"
	"reflect"
)

type Bar interface {
	test(int) string
}

type Foo struct {
	a string
}

func (f *Foo) test(b int) string {
	return f.a
}

type Writer interface {
	Write(string) string
}

type ResponseWriter struct {
	Foo *Foo `inject`
	t   int
}

func (*ResponseWriter) Write(s string) string {
	return s
}

type Request struct {
	Bar Bar `inject`
}

type Context struct {
	data string
}

type Controller struct {
	*Context `inject`
	Response  Writer `inject`
	response  Writer `inject`
	Response2 Writer `inject:"xyz"`
	Response3 Writer

	Request   Request `inject`
	action    string
}

func TestRegister(t *testing.T) {
	var foo1 Foo
	var foo2 Foo = Foo{}
	var foo3 *Foo = &Foo{}
	var foo4 []Foo = make([]Foo, 0)
	var foo5 [10]Foo
	var foo6 chan Foo
	var foo7 map[string]Foo
	var bar1 Bar = &Foo{}
	var bar2 *Bar = (*Bar)(nil)

	tests := []struct {
		value  interface{}
		result reflect.Type
	}{
		{100, reflect.TypeOf(int(0))},
		{"abc", reflect.TypeOf(string(""))},
		{true, reflect.TypeOf(bool(false))},
		{100, reflect.TypeOf(int(0))},
		{foo1, reflect.TypeOf(Foo{})},
		{foo2, reflect.TypeOf(Foo{})},
		{foo3, reflect.TypeOf(&Foo{})},
		{foo4, reflect.TypeOf(make([]Foo, 0))},
		{foo5, reflect.TypeOf([10]Foo{})},
		{foo6, reflect.TypeOf(make(chan Foo, 0))},
		{foo7, reflect.TypeOf(make(map[string]Foo))},
		{bar1, reflect.TypeOf(&Foo{})},
		{bar2, reflect.TypeOf((*Bar)(nil))},
	}

	for _, test := range tests {
		c := NewContainer()
		c.Register(test.value)
		if !c.HasRegistered(test.result) {
			t.Errorf("Register(%v) failed, expected %v is registered", test.value, test.result)
		}
	}
}

func TestRegisterInterface(t *testing.T) {
	var foo Foo
	var bar *Bar = (*Bar)(nil)

	c := NewContainer()
	barType := InterfaceOf(bar)
	c.RegisterAs(&foo, barType)
	if !c.HasRegistered(barType) {
		t.Errorf("RegisterAs(%v, %v) failed, expected %v is registered", &foo, barType, barType)
	}
	c.Unregister(barType)
	if c.HasRegistered(barType) {
		t.Errorf("barType is still registered, expected unregistered")
	}

	fooType := reflect.TypeOf(&foo)
	if c.HasRegistered(fooType) {
		t.Errorf("RegisterAs(%v, %v) failed, expected %v is NOT registered", &foo, barType, fooType)
	}

	defer func() {
		if e := recover(); e == nil {
			t.Errorf("Expected a panic when registering an incompatible object")
		}
	}()
	writerType := InterfaceOf((*Writer)(nil))
	c.RegisterAs(&foo, writerType)
}

func TestRegisterInterface2(t *testing.T) {
	var foo Foo
	fooType := reflect.TypeOf(&foo)
	writerType := InterfaceOf((*Writer)(nil))
	c := NewContainer()
	defer func() {
		if e := recover(); e == nil {
			t.Errorf("Expected a panic when registering an incompatible type")
		}
	}()
	c.RegisterAs(fooType, writerType)
}

func TestRegisterProvider(t *testing.T) {
	var f = func(Container) reflect.Value {
		return reflect.ValueOf(Foo{})
	}
	var bar *Bar = (*Bar)(nil)
	funcType := reflect.TypeOf(f)
	barType := InterfaceOf(bar)

	c := NewContainer()
	c.RegisterProvider(f, barType, true)
	if !c.HasRegistered(barType) {
		t.Errorf("RegisterProvider(%v, %v) failed, expected %v is registered", f, barType, barType)
	}
	if c.HasRegistered(funcType) {
		t.Errorf("RegisterProvider(%v, %v) failed, expected %v is NOT registered", f, barType, funcType)
	}
}

func TestBuildBasic(t *testing.T) {
	tests := []struct {
		rt     reflect.Type
		result interface{}
	}{
		{reflect.TypeOf(int(0)), 0},
		{reflect.TypeOf(string("abc")), ""},
		{reflect.TypeOf(Foo{}), Foo{}},
	}

	c := NewContainer()
	for _, test := range tests {
		v := c.Make(test.rt)
		if v != test.result {
			t.Errorf("Make(%v): expected %#v, got %#v", test.rt, test.result, v)
		}
	}

	v := c.Make(reflect.TypeOf(&Foo{}))
	if reflect.ValueOf(v).IsNil() {
		t.Errorf("Make(%v) = nil, expected not nil", &Foo{})
	}
}

func TestBuildComplex(t *testing.T) {
	c := NewContainer()

	var slice []int = c.Make(reflect.TypeOf([]int(nil))).([]int)
	if slice == nil || len(slice) != 0 {
		t.Errorf("Make([]int): expected []int{}, got %#v", slice)
	}

	var m map[string]Foo = c.Make(reflect.TypeOf(map[string]Foo{})).(map[string]Foo)
	if m == nil || len(m) != 0 {
		t.Errorf("Make(map[string]Foo): expected map[string]Foo{}, got %#v", slice)
	}

	var ch chan Foo = c.Make(reflect.TypeOf(make(chan Foo))).(chan Foo)
	if ch == nil || len(ch) != 0 {
		t.Errorf("Make(chan Foo): expected chan Foo{}, got %#v", ch)
	}
}

func TestBuildStruct(t *testing.T) {
	c := NewContainer()
	foo := Foo{"abc"}
	c.Register(foo)

	foo1 := c.Make(reflect.TypeOf(Foo{})).(Foo)
	if foo1.a != "abc" {
		t.Errorf("Make(Foo): expected Foo.a=%q, got %q", "abc", foo1.a)
	}

	// the registered version should remain unchanged (registering by value)
	foo1.a = "test"
	foo2 := c.Make(reflect.TypeOf(Foo{})).(Foo)
	if foo2.a != "abc" {
		t.Errorf("Make(Foo) after change: expected Foo.a=%q, got %q", "abc", foo2.a)
	}

	// registering by value doesn't mean registering the corresponding pointer
	foo3 := c.Make(reflect.TypeOf(&Foo{})).(*Foo)
	if foo3 != nil {
		t.Errorf("Make(Foo*) != nil, expected nil")
	}
}

func TestBuildStructPtr(t *testing.T) {
	c := NewContainer()
	foo := &Foo{"abc"}
	c.Register(foo)

	foo1 := c.Make(reflect.TypeOf(&Foo{})).(*Foo)
	if foo1.a != "abc" {
		t.Errorf("Make(*Foo): expected Foo.a=%q, got %q", "abc", foo1.a)
	}

	// the registered version should be unchanged (registering by pointer)
	foo1.a = "test"
	foo2 := c.Make(reflect.TypeOf(&Foo{})).(*Foo)
	if foo2.a != "test" {
		t.Errorf("Make(*Foo) after change: expected Foo.a=%q, got %q", "test", foo2.a)
	}

	// registering by value doesn't mean registering the corresponding pointer
	foo3 := c.Make(reflect.TypeOf(Foo{})).(Foo)
	if foo3.a != "test" {
		t.Errorf("Make(Foo).a = %q, expected %q", foo3.a, "abc")
	}
}

func TestBuildInterface(t *testing.T) {
	c := NewContainer()
	w := ResponseWriter{t: 1}
	writer := InterfaceOf((*Writer)(nil))
	c.RegisterAs(&w, writer)

	// make interface
	r := c.Make(writer).(*ResponseWriter)
	if r.t != 1 {
		t.Errorf("Make(Writer).t = %v, expected 1", r.t)
	}

	// make pointer
	r2 := c.Make(reflect.TypeOf(&w)).(*ResponseWriter)
	if r2 == nil {
		t.Errorf("Make(*ResponseWriter) = nil, expected not nil")
	}

	// making an unregistered concrete struct should result in zero struct
	r3 := c.Make(reflect.TypeOf(w)).(ResponseWriter)
	if r3.t != 0 {
		t.Errorf("Make(ResponseWriter).t = %v, expected 0", r3.t)
	}

	// making an unregistered interface should result in nil
	r4 := c.Make(InterfaceOf((*Bar)(nil)))
	if r4 != nil {
		t.Errorf("Make(Bar) = %#v, expected nil", r4)
	}
}

func TestBuildProvider(t *testing.T) {
	// shared
	c := NewContainer()
	c.RegisterProvider(func(Container) reflect.Value {
		return reflect.ValueOf(&Foo{"abc"})
	}, reflect.TypeOf(&Foo{}), true)
	foo1 := c.Make(reflect.TypeOf(&Foo{})).(*Foo)
	if foo1.a != "abc" {
		t.Errorf("Make(*Foo).a = %q, expected %q", foo1.a, "abc")
	}
	foo1.a = "xyz"
	foo2 := c.Make(reflect.TypeOf(&Foo{})).(*Foo)
	if foo1.a != foo2.a {
		t.Errorf("Changed shared Foo, Make(*Foo).a = %q, expected %q", foo2.a, foo1.a)
	}

	// not shared
	c = NewContainer()
	c.RegisterProvider(func(Container) reflect.Value {
		return reflect.ValueOf(&Foo{"abc"})
	}, reflect.TypeOf(&Foo{}), false)
	foo3 := c.Make(reflect.TypeOf(&Foo{})).(*Foo)
	if foo3.a != "abc" {
		t.Errorf("Make(*Foo).a = %q, expected %q", foo3.a, "abc")
	}
	foo3.a = "xyz"
	foo4 := c.Make(reflect.TypeOf(&Foo{})).(*Foo)
	if foo3.a == foo4.a {
		t.Errorf("Changed non-shared Foo, Make(*Foo).a = %q, expected %q", foo4.a, "abc")
	}
}

func TestBuildInject(t *testing.T) {
	barType := InterfaceOf((*Bar)(nil))
	writerType := InterfaceOf((*Writer)(nil))

	c := NewContainer()
	c.Register(&Context{"abc"})
	c.RegisterAs(&Foo{"123"}, barType)
	c.RegisterAs(reflect.TypeOf(&ResponseWriter{}), writerType)

	con := c.Make(reflect.TypeOf(Controller{})).(Controller)
	if con.action != "" {
		t.Errorf("Controller.action = %q, expected %q", con.action, "")
	}
	if con.data != "abc" {
		t.Errorf("Controller.data = %q, expected %q", con.data, "abc")
	}
	if con.Request.Bar.test(0) != "123" {
		t.Errorf("Controller.request.bar.test() = %q, expected %q", con.Request.Bar.test(0), "123")
	}
}

func TestInject(t *testing.T) {
	writerType := InterfaceOf((*Writer)(nil))

	c := NewContainer()
	c.Register(&Context{"abc"})
	c.RegisterAs(&ResponseWriter{t: 123}, writerType)
	c.RegisterAs(&Foo{"xyz"}, InterfaceOf((*Bar)(nil)))
	con := &Controller{}
	c.Inject(con)
	// anonymous field injection
	if con.data != "abc" {
		t.Errorf("Controller.data = %q, expected %q", con.data, "abc")
	}
	// valid injection
	if con.Response == nil {
		t.Errorf("Controller.Response should not be nil")
	}
	if con.Response.(*ResponseWriter).t != 123 {
		t.Errorf("Controller.Response.t = %q, expected %q", con.Response.(*ResponseWriter).t, 123)
	}
	// inject struct tag
	if con.Response2 == nil {
		t.Errorf("Controller.Response2 should not be nil")
	}
	// field not marked with "inject" should not be injected
	if con.Response3 != nil {
		t.Errorf("Controller.Response3 should be nil")
	}
	// unexported should not be injected
	if con.response != nil {
		t.Errorf("Controller.response should be nil")
	}

	// recursive injection
	if con.Request.Bar == nil {
		t.Errorf("Controller.Request.bar = nil, expected not nil")
	}
	if con.Request.Bar.(*Foo).a != "xyz" {
		t.Errorf("Controller.Request.bar.(*Foo).a = %q, expected %q", con.Request.Bar.(*Foo).a, "xyz")
	}

	// nothing should happen for injecting a non-struct variable
	c.Inject(1)
}

func TestCall(t *testing.T) {
	writerType := InterfaceOf((*Writer)(nil))

	c := NewContainer()
	w := ResponseWriter{t: 1}
	context := Context{"abc"}
	c.RegisterAs(&w, writerType)
	c.Register(&context)

	f := func(cc *Context, w Writer, s string) (string, string, string) {
		return cc.data, w.Write("test"), s
	}
	result := c.Call(f)
	if len(result) != 3 {
		t.Error("The return result should contain 3 values.")
	}
	if result[0] != "abc" || result[1] != "test" {
		t.Errorf("The return result was (%q, %q, %q), expected (%q, %q, %q)", result[0], result[1], result[2], "abc", "test", "")
	}
}

func TestParent(t *testing.T) {
	c := NewContainer()
	if c.ParentContainer() != nil {
		t.Error("ParentContainer() != nil, expected nil")
	}

	f := c.Make(reflect.TypeOf(Foo{})).(Foo)
	if f.a != "" {
		t.Error("Build(Foo) returns non-zero Foo struct, expected zero")
	}

	p := NewContainer()
	p.Register(Foo{"abc"})
	c.SetParentContainer(p)
	if c.ParentContainer() == nil {
		t.Error("ParentContainer() == nil, expected not nil")
	}
	f = c.Make(reflect.TypeOf(Foo{})).(Foo)
	if f.a != "abc" {
		t.Errorf("Build(Foo).a=%q, expected %q", f.a, "abc")
	}
}

func TestInterfaceOf(t *testing.T) {
	defer func() {
		if e := recover(); e == nil {
			t.Errorf("Expected a panic when calling InterfaceOf() with a non-interface")
		}
	}()
	InterfaceOf(reflect.TypeOf(Foo{}))
}
<|endoftext|>


<commit_before>// +build main

/*
   Usage:    a.out 1-212-736-5000 >/dev/audio
*/
package main

import . "github.com/strickyak/rxtx/mulaw"

import (
	"math"
	"os"
	"strconv"
)

const RATE = 8000
const GAIN = 1000

func parseFloat(s string) float64 {
	f, err := strconv.ParseFloat(s, 64)
	if err != nil { panic(err) }
	return f
}

func toneSample(freq, gain float64, t int) int16 {
	w := float64(t) * float64(freq) / float64(RATE) * 2.0 * math.Pi
	return int16(gain * math.Sin(w))
}

func dtmf(r rune) (float64, float64) {
	switch (r) {
	case '1': return 679, 1209
	case '2': return 679, 1336
	case '3': return 679, 1477
	case 'a': return 679, 1633
	case 'A': return 679, 1633
	case '4': return 770, 1209
	case '5': return 770, 1336
	case '6': return 770, 1477
	case 'b': return 770, 1633
	case 'B': return 770, 1633
	case '7': return 852, 1209
	case '8': return 852, 1336
	case '9': return 852, 1477
	case 'c': return 852, 1633
	case 'C': return 852, 1633
	case '*': return 941, 1209
	case '0': return 941, 1336
	case '#': return 941, 1477
	case 'd': return 941, 1633
	case 'D': return 941, 1633
	}
	return 0, 0
}

func main() {
	digits := os.Args[1]       // e.g. "411"
	buf := make([]byte, 1)

	for _, digit := range digits {
		for t := 0; t < int(0.5*RATE); t++ {
			f1, f2 := dtmf(digit)
			x1 := toneSample(f1, GAIN, t)
			x2 := toneSample(f2, GAIN, t)
			b := EncodeMulaw16(x1 + x2)
			buf[0] = b
			n, err := os.Stdout.Write(buf)
			if err != nil { panic(err) }
			if n != 1 { panic(n) }
		}
		for t := 0; t < int(0.5*RATE); t++ {
			buf[0] = 0
			n, err := os.Stdout.Write(buf)
			if err != nil { panic(err) }
			if n != 1 { panic(n) }
		}
	}
}
<commit_msg>adjustable TONE_LENGTH<commit_after>// +build main

/*
   Usage:    a.out 1-212-736-5000 >/dev/audio
*/
package main

import . "github.com/strickyak/rxtx/mulaw"

import (
	"math"
	"os"
	"strconv"
)

const TONE_LENGTH = 0.15

const RATE = 8000
const GAIN = 1000

func parseFloat(s string) float64 {
	f, err := strconv.ParseFloat(s, 64)
	if err != nil { panic(err) }
	return f
}

func toneSample(freq, gain float64, t int) int16 {
	w := float64(t) * float64(freq) / float64(RATE) * 2.0 * math.Pi
	return int16(gain * math.Sin(w))
}

func dtmf(r rune) (float64, float64) {
	switch (r) {
	case '1': return 679, 1209
	case '2': return 679, 1336
	case '3': return 679, 1477
	case 'a': return 679, 1633
	case 'A': return 679, 1633
	case '4': return 770, 1209
	case '5': return 770, 1336
	case '6': return 770, 1477
	case 'b': return 770, 1633
	case 'B': return 770, 1633
	case '7': return 852, 1209
	case '8': return 852, 1336
	case '9': return 852, 1477
	case 'c': return 852, 1633
	case 'C': return 852, 1633
	case '*': return 941, 1209
	case '0': return 941, 1336
	case '#': return 941, 1477
	case 'd': return 941, 1633
	case 'D': return 941, 1633
	}
	return 0, 0
}

func main() {
	digits := os.Args[1]       // e.g. "411"
	buf := make([]byte, 1)

	for _, digit := range digits {
		for t := 0; t < int(TONE_LENGTH*RATE); t++ {
			f1, f2 := dtmf(digit)
			x1 := toneSample(f1, GAIN, t)
			x2 := toneSample(f2, GAIN, t)
			b := EncodeMulaw16(x1 + x2)
			buf[0] = b
			n, err := os.Stdout.Write(buf)
			if err != nil { panic(err) }
			if n != 1 { panic(n) }
		}
		for t := 0; t < int(TONE_LENGTH*RATE); t++ {
			buf[0] = 0
			n, err := os.Stdout.Write(buf)
			if err != nil { panic(err) }
			if n != 1 { panic(n) }
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"sync"
)

func main() {
	var wg sync.WaitGroup
	ctx := NewContext()

	counter := NewCounter(ctx, &wg)
	c := counter.GetSource()

	enlarger := NewEnlarger(ctx, &wg, c, 2)
	r := enlarger.GetSource()

	fmt.Println(<-r)
	fmt.Println(<-r)
	fmt.Println(<-r)
	ctx.Stop()
	wg.Wait()
}
<commit_msg>Simple cli flag.<commit_after>package main

import (
	"flag"
	"fmt"
	"sync"
)

func main() {
	var wg sync.WaitGroup

	factor := flag.Int("factor", 0, "multiplier for enlarger")
	flag.Parse()

	ctx := NewContext()

	counter := NewCounter(ctx, &wg)
	c := counter.GetSource()

	enlarger := NewEnlarger(ctx, &wg, c, *factor)
	r := enlarger.GetSource()

	fmt.Println(<-r)
	fmt.Println(<-r)
	fmt.Println(<-r)
	ctx.Stop()
	wg.Wait()
}
<|endoftext|>


<commit_before>package audio

import "sync"

// OnDataCb is a callback function which will be called by an audio source
// when new data is available
type OnDataCb func(Msg)

// Source is the interface which is implemented by an audio source. This
// could be streaming data received from a network connection, a local
// audio source (e.g. microphone) or a local file.
type Source interface {
	Start() error
	Stop() error
	Close() error
	SetCb(OnDataCb)
}

// Node is the interface for an audio node. Nodes are typically located
// in an audio Chain between the Source and the Sink and perform custom
// processing on (Audio)Msgs.
type Node interface {
	Write(Msg) error // Write data into the Node
	SetCb(OnDataCb)  // Set the callback which will be executed when processing has finished.
}

// Sink is the interface which is implemented by an audio sink. This could
// be an Audio player or a file for recording.
type Sink interface {
	Start() error
	Stop() error
	Close() error
	SetVolume(float32)
	Volume() float32
	Write(Msg) error
	Flush()
}

// Token contains a sync.Waitgroup and is used with an audio sink. The
// token will indicate the application to wait until further audio buffers
// can be enqueued into the sink.
type Token struct {
	*sync.WaitGroup
}

// Msg contains an audio buffer with it's metadata.
type Msg struct {
	Data       []float32
	Samplerate float64                // samplerate, e.g. 48000Hz
	Channels   int                    // most of the time either 1 (mono) or 2 (stereo)
	Frames     int                    // Number of Frames in the buffer
	EOF        bool                   // End of File
	Metadata   map[string]interface{} // storage for passing any kind of data along the audio Msg
}
<commit_msg>improved docstrings<commit_after>package audio

import "sync"

// OnDataCb is a callback function which will be called by an audio source
// when new data is available
type OnDataCb func(Msg)

// Source is the interface which is implemented by an audio source. This
// could be streaming data received from a network connection, a local
// audio source (e.g. microphone) or a local file.
type Source interface {
	Start() error
	Stop() error
	Close() error
	SetCb(OnDataCb)
}

// Node is the interface for an audio node. Nodes are typically located
// in an audio Chain between the Source and the Sink and perform custom
// processing on (Audio)Msgs.
type Node interface {
	Write(Msg) error // Write data into the Node
	SetCb(OnDataCb)  // Set the callback which will be executed when processing has finished.
}

// Sink is the interface which is implemented by an audio sink. This could
// be an Audio player or a file for recording.
type Sink interface {
	Start() error
	Stop() error
	Close() error
	SetVolume(float32)
	Volume() float32
	Write(Msg) error
	Flush()
}

// Token contains a sync.Waitgroup and is used with an audio sink. The
// token will indicate the application to wait until further audio buffers
// can be enqueued into the sink.
type Token struct {
	*sync.WaitGroup
}

// Msg contains an audio buffer with it's metadata. Msgs are internally used to
// pass the data from the source, through the audio nodes to the audio sink(s).
type Msg struct {
	Data       []float32              // audio data, float32 interleaved (if stereo)
	Samplerate float64                // samplerate, e.g. 48000Hz
	Channels   int                    // most of the time either 1 (mono) or 2 (stereo)
	Frames     int                    // Number of Frames in the buffer
	EOF        bool                   // End of File
	Metadata   map[string]interface{} // storage for passing any kind of data along the audio Msg
}
<|endoftext|>