<commit_before>package main

import (
	"flag"
	"log"
	"os"
	"time"
)

const (
	checkInterval    = 2 * time.Second
	remakeErrorSleep = 5 * time.Second
	exitErrorSleep   = 5 * time.Second
)

var (
	gracePeriod time.Duration
	readyMode   bool
)

func main() {
	// Parse the command line arguments.
	flag.BoolVar(
		&readyMode,
		"ready",
		false,
		"Send a ready signal and then quit",
	)
	flag.DurationVar(
		&gracePeriod,
		"grace",
		10*time.Second,
		"Grace period for a command to start up",
	)
	flag.Parse()

	// If "remake -ready" was run, send the ready signal and then exit.
	if readyMode {
		err := SendReadySignal()
		if err != nil {
			os.Exit(1)
		}
		os.Exit(0)
	}

	// Handle when there is no target in the command line arguments.
	// Remake will use the default target if no target is specified.
	goals := flag.Args()
	if len(goals) == 0 {
		goals = append(goals, "")
	}

	// Handle signals from "remake -ready".
	var ready chan (os.Signal)
	if len(goals) == 1 {
		// When managing one make command, listen for the signal.
		ready = ReceiveReadySignal()
	} else {
		// When there are multiple make commands, and a ready signal is
		// received, which make command was it from? Unix does support
		// figuring this out, but the Go library doesn't. So the ready
		// signals will be ignored in this case. Use a dummy channel
		// that will never receive a signal.
		ready = make(chan os.Signal)
	}

	// And so it begins. Start one goroutine per goal.
	for _, goal := range goals {
		go remake(goal, ready)
	}

	// Block execution forever and let the goroutines work.
	block := make(chan bool)
	<-block
}

// remake runs the main loop for one make command.
func remake(goal string, ready chan os.Signal) {
	// Use an infinite loop. The manageMake starts a make command, and
	// will only return when it needs to be started again, for any reason.
	for {
		manageMake(goal, ready)
	}
}

// manageMake runs a make command, watches for changes, and kills the command
// if/when changes are found. It returns only when it should be started again.
func manageMake(goal string, ready chan os.Signal) {

	// Keep track of when the make command was run. This will be changed
	// to be just after the grace mode finishes, if the command doesn't
	// finish first, so that changes made by the command won't trigger
	// itself to be restarted. This only affects make commands for
	// phony targets.
	startTime := time.Now()

	// Run the underlying make command.
	var makeCmd *Cmd
	if len(goal) == 0 {
		makeCmd = NewCommand("make")
	} else {
		makeCmd = NewCommand("make", goal)
	}
	if err := makeCmd.Start(); err != nil {
		// It failed to start.
		log.Printf(red("Remake: %s: %s"), makeCmd.String(), err)
		time.Sleep(remakeErrorSleep)
		// Return so it will try to run the command again.
		return
	}

	// Create the query that can check for changes.
	query := NewQuery(goal)

	// Run in grace mode at first.
	graceMode := true
	graceModeEnd := time.After(gracePeriod)
	running := true

	// Start monitoring.
	for {
		select {
		case <-ready:
			// Ready signal received, exit grace mode.
			graceMode = false
			startTime = time.Now()

		case err := <-makeCmd.Finished():
			// Command finished.
			if err != nil {
				log.Printf(red("Remake %s: %s"), err, makeCmd.String())
			}
			graceMode = false
			running = false

		case <-time.After(checkInterval):
			// Regularly check for changes.
			changed, err := query.Run(startTime)
			if err != nil {
				// Error running the query to check for changes.
				// Sleep and let it try again.
				log.Printf(red("Remake %s: %s"), err, query.String())
				time.Sleep(remakeErrorSleep)
			} else if graceMode {
				if changed {
					// Push the start time forward. Trying to minimize
					// the window for a race condition to occur.
					startTime = time.Now()
				} else {
					// Everything is up to date, so it must be ready to
					// leave grace mode and start monitoring normally.
					graceMode = false
				}
			} else if changed {
				// Detected changes outside of grace mode.
				// Kill the process if it is still running.
				if running {
					if err := makeCmd.Kill(); err != nil {
						log.Printf(red("Remake: kill: %s"), err)
					}
				}
				// Return so it can run the command again.
				return
			}

		case <-graceModeEnd:
			if graceMode && running {
				// This might happen due to a race condition when changing
				// files rapidly. The "remake --ready" command was created
				// to avoid this, and it should not allow this to happen
				// unless builds actually take this long to finish running.
				log.Printf(
					red("Remake initializing for too long: %s"),
					makeCmd.String(),
				)
				makeCmd.Kill()
				// Return so it can run the command again.
				return
			}
		}
	}
}

// red makes text red.
func red(s string) string {
	const (
		redColor   = "\033[0;31m"
		resetColor = "\033[0m"
	)
	return redColor + s + resetColor
}
<commit_msg>Used a lock to start make commands sequentially.<commit_after>package main

import (
	"flag"
	"log"
	"os"
	"sync"
	"time"
)

const (
	checkInterval    = 2 * time.Second
	remakeErrorSleep = 5 * time.Second
	exitErrorSleep   = 5 * time.Second
)

var (
	gracePeriod time.Duration
	readyMode   bool
	buildMutex  sync.Mutex
)

func main() {
	// Parse the command line arguments.
	flag.BoolVar(
		&readyMode,
		"ready",
		false,
		"Send a ready signal and then quit",
	)
	flag.DurationVar(
		&gracePeriod,
		"grace",
		10*time.Second,
		"Grace period for a command to start up",
	)
	flag.Parse()

	// If "remake -ready" was run, send the ready signal and then exit.
	if readyMode {
		err := SendReadySignal()
		if err != nil {
			os.Exit(1)
		}
		os.Exit(0)
	}

	// Handle when there is no target in the command line arguments.
	// Remake will use the default target if no target is specified.
	goals := flag.Args()
	if len(goals) == 0 {
		goals = append(goals, "")
	}

	// Handle signals from "remake -ready".
	var ready chan (os.Signal)
	if len(goals) == 1 {
		// When managing one make command, listen for the signal.
		ready = ReceiveReadySignal()
	} else {
		// When there are multiple make commands, and a ready signal is
		// received, which make command was it from? Unix does support
		// figuring this out, but the Go library doesn't. So the ready
		// signals will be ignored in this case. Use a dummy channel
		// that will never receive a signal.
		ready = make(chan os.Signal)
	}

	// And so it begins. Start one goroutine per goal.
	for _, goal := range goals {
		go remake(goal, ready)
	}

	// Block execution forever and let the goroutines work.
	block := make(chan bool)
	<-block
}

// remake runs the main loop for one make command.
func remake(goal string, ready chan os.Signal) {
	// Use an infinite loop. The manageMake starts a make command, and
	// will only return when it needs to be started again, for any reason.
	for {
		manageMake(goal, ready)
	}
}

// manageMake runs a make command, watches for changes, and kills the command
// if/when changes are found. It returns only when it should be started again.
func manageMake(goal string, ready chan os.Signal) {
	// Prevent multiple make commands from starting up at the same time.
	// Otherwise, separate make commands with shared dependencies could
	// build the same targets at the same time. This lock will last until
	// either the command exits, or it leaves grace mode and starts the
	// normal monitoring behavior.
	buildMutex.Lock()
	locked := true
	unlock := func() {
		if locked {
			buildMutex.Unlock()
			locked = false
		}
	}
	defer unlock()

	// Keep track of when the make command was run. This will be changed
	// to be just after the grace mode finishes, if the command doesn't
	// finish first, so that changes made by the command won't trigger
	// itself to be restarted. This only affects make commands for
	// phony targets.
	startTime := time.Now()

	// Run the underlying make command.
	var makeCmd *Cmd
	if len(goal) == 0 {
		makeCmd = NewCommand("make")
	} else {
		makeCmd = NewCommand("make", goal)
	}
	if err := makeCmd.Start(); err != nil {
		// It failed to start.
		log.Printf(red("Remake: %s: %s"), makeCmd.String(), err)
		time.Sleep(remakeErrorSleep)
		// Return so it will try to run the command again.
		return
	}

	// Create the query that can check for changes.
	query := NewQuery(goal)

	// Run in grace mode at first.
	running := true
	graceMode := true
	graceModeEnd := time.After(gracePeriod)
	leaveGraceMode := func() {
		graceMode = false
		unlock()
	}

	// Start monitoring.
	for {
		select {
		case <-ready:
			// Ready signal received.
			startTime = time.Now()
			leaveGraceMode()

		case err := <-makeCmd.Finished():
			// Command finished.
			if err != nil {
				log.Printf(red("Remake %s: %s"), err, makeCmd.String())
			}
			running = false
			leaveGraceMode()

		case <-time.After(checkInterval):
			// Regularly check for changes.
			changed, err := query.Run(startTime)
			if err != nil {
				// Error running the query to check for changes.
				// Sleep and let it try again.
				log.Printf(red("Remake %s: %s"), err, query.String())
				time.Sleep(remakeErrorSleep)
			} else if graceMode {
				if changed {
					// Push the start time forward. Trying to minimize
					// the window for a race condition to occur.
					startTime = time.Now()
				} else {
					// Everything is up to date, so it must be ready to
					// leave grace mode and start monitoring normally.
					leaveGraceMode()
				}
			} else if changed {
				// Detected changes outside of grace mode.
				// Kill the process if it is still running.
				if running {
					if err := makeCmd.Kill(); err != nil {
						log.Printf(red("Remake: kill: %s"), err)
					}
				}
				// Return so it can run the command again.
				return
			}

		case <-graceModeEnd:
			if graceMode && running {
				// This might happen due to a race condition when changing
				// files rapidly. The "remake --ready" command was created
				// to avoid this, and it should not allow this to happen
				// unless builds actually take this long to finish running.
				log.Printf(
					red("Remake initializing for too long: %s"),
					makeCmd.String(),
				)
				makeCmd.Kill()
				// Return so it can run the command again.
				return
			}
		}
	}
}

// red makes text red.
func red(s string) string {
	const (
		redColor   = "\033[0;31m"
		resetColor = "\033[0m"
	)
	return redColor + s + resetColor
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"os"
	"regexp"

	"github.com/cloudfoundry/cli/cf/configuration/config_helpers"
	"github.com/cloudfoundry/cli/cf/configuration/core_config"
	"github.com/cloudfoundry/cli/plugin"
)

type Application struct {
	Name string
}

type BlueGreenDeployPlugin struct {
	Connection plugin.CliConnection
}

func (p *BlueGreenDeployPlugin) Run(cliConnection plugin.CliConnection, args []string) {
	p.Connection = cliConnection

	if len(args) < 2 {
		fmt.Printf("appname must be specified")
		os.Exit(1)
	}

	appName := args[1]
	err := p.DeleteOldAppVersions(appName)
	if err != nil {
		fmt.Printf("Could not delete old app version - %s", err.Error())
		os.Exit(1)
	}

	fmt.Println("Hello world! The sky is all blue/green.")
}

func (p *BlueGreenDeployPlugin) GetMetadata() plugin.PluginMetadata {
	return plugin.PluginMetadata{
		Name: "blue-green-deploy",
		Version: plugin.VersionType{
			Major: 0,
			Minor: 1,
			Build: 0,
		},
		Commands: []plugin.Command{
			{
				Name:     "blue-green-deploy",
				Alias:    "bgd",
				HelpText: "Do zero-time deploys in a non-sucky way",
			},
		},
	}
}

func (p *BlueGreenDeployPlugin) oldAppVersionList(appName string) (oldApps []Application, err error) {
	apps, err := p.appsInCurrentSpace()
	if err != nil {
		return
	}
	oldApps = filterOldApps(appName, apps)
	return
}

func (p *BlueGreenDeployPlugin) deleteApps(apps []Application) error {
	for _, app := range apps {
		if _, err := p.Connection.CliCommand("delete", app.Name, "-f", "-r"); err != nil {
			return err
		}
	}

	return nil
}

func (p *BlueGreenDeployPlugin) DeleteOldAppVersions(appName string) error {
	appNames, err := p.oldAppVersionList(appName)
	if err != nil {
		return err
	}
	return p.deleteApps(appNames)
}

func (p *BlueGreenDeployPlugin) appsInCurrentSpace() ([]Application, error) {
	path := fmt.Sprintf("/v2/spaces/%s/summary", getSpaceGuid())

	output, err := p.Connection.CliCommandWithoutTerminalOutput("curl", path)
	if err != nil {
		return nil, err
	}

	apps := struct {
		Apps []Application
	}{}

	json.Unmarshal([]byte(output[0]), &apps)
	return apps.Apps, nil
}

func getSpaceGuid() string {
	configRepo := core_config.NewRepositoryFromFilepath(config_helpers.DefaultFilePath(), func(err error) {
		if err != nil {
			fmt.Printf("Config error: %s", err)
		}
	})

	return configRepo.SpaceFields().Guid
}

func filterOldApps(appName string, apps []Application) (oldApps []Application) {
	r := regexp.MustCompile(fmt.Sprintf("%s-[0-9]{14}-old", appName))
	oldApps = []Application{}
	for _, app := range apps {
		if r.MatchString(app.Name) {
			oldApps = append(oldApps, app)
		}
	}
	return
}

func main() {
	plugin.Start(&BlueGreenDeployPlugin{})
}
<commit_msg>Simplify control flow<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"os"
	"regexp"

	"github.com/cloudfoundry/cli/cf/configuration/config_helpers"
	"github.com/cloudfoundry/cli/cf/configuration/core_config"
	"github.com/cloudfoundry/cli/plugin"
)

type Application struct {
	Name string
}

type BlueGreenDeployPlugin struct {
	Connection plugin.CliConnection
}

func (p *BlueGreenDeployPlugin) Run(cliConnection plugin.CliConnection, args []string) {
	p.Connection = cliConnection

	if len(args) < 2 {
		fmt.Printf("appname must be specified")
		os.Exit(1)
	}

	appName := args[1]
	err := p.DeleteOldAppVersions(appName)
	if err != nil {
		fmt.Printf("Could not delete old app version - %s", err.Error())
		os.Exit(1)
	}

	fmt.Println("Hello world! The sky is all blue/green.")
}

func (p *BlueGreenDeployPlugin) GetMetadata() plugin.PluginMetadata {
	return plugin.PluginMetadata{
		Name: "blue-green-deploy",
		Version: plugin.VersionType{
			Major: 0,
			Minor: 1,
			Build: 0,
		},
		Commands: []plugin.Command{
			{
				Name:     "blue-green-deploy",
				Alias:    "bgd",
				HelpText: "Do zero-time deploys in a non-sucky way",
			},
		},
	}
}

func (p *BlueGreenDeployPlugin) deleteApps(apps []Application) error {
	for _, app := range apps {
		if _, err := p.Connection.CliCommand("delete", app.Name, "-f", "-r"); err != nil {
			return err
		}
	}

	return nil
}

func (p *BlueGreenDeployPlugin) DeleteOldAppVersions(appName string) error {
	apps, err := p.appsInCurrentSpace()
	if err != nil {
		return err
	}
	return p.deleteApps(FilterOldApps(appName, apps))
}

func (p *BlueGreenDeployPlugin) appsInCurrentSpace() ([]Application, error) {
	path := fmt.Sprintf("/v2/spaces/%s/summary", getSpaceGuid())

	output, err := p.Connection.CliCommandWithoutTerminalOutput("curl", path)
	if err != nil {
		return nil, err
	}

	apps := struct {
		Apps []Application
	}{}

	json.Unmarshal([]byte(output[0]), &apps)
	return apps.Apps, nil
}

func getSpaceGuid() string {
	configRepo := core_config.NewRepositoryFromFilepath(config_helpers.DefaultFilePath(), func(err error) {
		if err != nil {
			fmt.Printf("Config error: %s", err)
		}
	})

	return configRepo.SpaceFields().Guid
}

func filterOldApps(appName string, apps []Application) (oldApps []Application) {
	r := regexp.MustCompile(fmt.Sprintf("%s-[0-9]{14}-old", appName))
	oldApps = []Application{}
	for _, app := range apps {
		if r.MatchString(app.Name) {
			oldApps = append(oldApps, app)
		}
	}
	return
}

func main() {
	plugin.Start(&BlueGreenDeployPlugin{})
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"time"

	"github.com/duythinht/gelf/client"
)

func errOrNot(err error) {
	if err != nil {
		fmt.Println("Error", err)
		os.Exit(1)
	}
}

var msgTpl = `
{
  "version": "1.1",
  "host": "chuck...1",
  "short_message": "A short message %d",
  "full_message": "Backtrace here\n\nmore stuff",
  "timestamp": %d,
  "level": 1,
  "_user_id": %d,
  "_some_info": "foo",
  "_some_env_var": "bar"
}
`

func main() {

	count := 0

	c := client.New(client.Config{
		GraylogHost: "10.60.6.48",
		GraylogPort: 13000,
	})

	for {
		count++
		now := time.Now().Unix()
		message := fmt.Sprintf(msgTpl, count, now, count)

		c.Send(message)

		time.Sleep(time.Second)
	}
}
<commit_msg>Remove main<commit_after><|endoftext|>


<commit_before>package querybuilder

type CommonQueryEvents struct {
}

func (e CommonQueryEvents) BeforeInsert() error {
	return nil
}

func (e CommonQueryEvents) BeforeUpdate() error {
	return nil
}

func (e CommonQueryEvents) AfterFind() error {
	return nil
}

func (e CommonQueryEvents) AfterDelete() error {
	return nil
}
<commit_msg>udpate<commit_after>package querybuilder

type CommonQueryEvents struct {
}

func (e CommonQueryEvents) BeforeInsert() error {
	return nil
}

func (e CommonQueryEvents) AfterInsert() error {
	return nil
}

func (e CommonQueryEvents) BeforeUpdate() error {
	return nil
}

func (e CommonQueryEvents) AfterUpdate() error {
	return nil
}

func (e CommonQueryEvents) AfterFind() error {
	return nil
}

func (e CommonQueryEvents) AfterDelete() error {
	return nil
}
<|endoftext|>


<commit_before>package makex

import (
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"

	"code.google.com/p/rog-go/parallel"
)

// TargetsNeedingBuild returns an ordered list of target sets
func (c *Config) NewMaker(mf *Makefile, goals ...string) *Maker {
	m := &Maker{
		mf:     mf,
		goals:  goals,
		Config: c,
	}
	m.buildDAG()
	return m
}

type Maker struct {
	mf     *Makefile
	goals  []string
	dag    map[string][]string
	topo   [][]string
	cycles map[string][]string

	// RuleOutput specifies the writers to receive the stdout and stderr output
	// from executing a rule's recipes. If RuleOutput is nil, os.Stdout and
	// os.Stderr are used, respectively.
	RuleOutput func(Rule) (out io.Writer, err io.Writer, logger *log.Logger)

	*Config
}

func (m *Maker) buildDAG() {
	// topological sort taken from
	// http://rosettacode.org/wiki/Topological_sort#Go.

	if m.dag == nil || m.cycles == nil {
		m.dag = make(map[string][]string)
		m.cycles = make(map[string][]string)
	}

	seen := make(map[string]struct{})
	queue := append([]string{}, m.goals...)
	for {
		if len(queue) == 0 {
			break
		}
		origLen := len(queue)
		for _, target := range queue {
			if _, seen := seen[target]; seen {
				continue
			}
			seen[target] = struct{}{}

			rule := m.mf.Rule(target)
			if rule == nil {
				continue
			}
			m.dag[target] = append([]string{}, rule.Prereqs()...)
			for _, dep := range rule.Prereqs() {
				// make a node for the prereq target even if it isn't defined
				queue = append(queue, dep)
				m.dag[dep] = m.dag[dep]
			}
		}
		queue = queue[origLen:]
	}

	// topological sort on the DAG
	for len(m.dag) > 0 {

		// collect targets with no dependencies
		var zero []string
		for target, prereqs := range m.dag {
			if len(prereqs) == 0 {
				zero = append(zero, target)
				delete(m.dag, target)
			}
		}

		// cycle detection
		if len(zero) == 0 {
			// collect un-orderable dependencies
			cycle := make(map[string]bool)
			for _, prereqs := range m.dag {
				for _, dep := range prereqs {
					cycle[dep] = true
				}
			}

			// mark targets with un-orderable dependencies
			for target, prereqs := range m.dag {
				if cycle[target] {
					m.cycles[target] = prereqs
				}
			}
			return
		}

		// output a set that can be processed concurrently
		m.topo = append(m.topo, zero)

		// remove edges (dependencies) from dg
		for _, remove := range zero {
			for target, prereqs := range m.dag {
				for i, dep := range prereqs {
					if dep == remove {
						copy(prereqs[i:], prereqs[i+1:])
						m.dag[target] = prereqs[:len(prereqs)-1]
						break
					}
				}
			}
		}
	}
}

func (m *Maker) TargetSets() [][]string {
	return m.topo
}

func (m *Maker) TargetSetsNeedingBuild() ([][]string, error) {
	for _, goal := range m.goals {
		if rule := m.mf.Rule(goal); rule == nil {
			return nil, errNoRuleToMakeTarget(goal)
		}
		if deps, isCycle := m.cycles[goal]; isCycle {
			return nil, errCircularDependency(goal, deps)
		}
	}

	targetSets := make([][]string, 0)
	for _, targetSet := range m.topo {
		var targetsNeedingBuild []string
		for _, target := range targetSet {
			exists, err := m.pathExists(target)
			if err != nil {
				return nil, err
			}
			if !exists {
				rule := m.mf.Rule(target)
				if rule == nil {
					return nil, errNoRuleToMakeTarget(target)
				}
				targetsNeedingBuild = append(targetsNeedingBuild, target)
			}
		}
		if len(targetsNeedingBuild) > 0 {
			targetSets = append(targetSets, targetsNeedingBuild)
		}
	}
	return targetSets, nil
}

// DryRun prints information about what targets *would* be built if Run() was
// called.
func (m *Maker) DryRun(w io.Writer) error {
	targetSets, err := m.TargetSetsNeedingBuild()
	if err != nil {
		return err
	}
	if len(targetSets) == 0 {
		fmt.Fprintln(w, "No target sets need building.")
	}
	for i, targetSet := range targetSets {
		if i != 0 {
			fmt.Fprintln(w)
		}
		fmt.Fprintf(w, "========= TARGET SET %d (%d targets)\n", i, len(targetSet))
		for _, target := range targetSet {
			fmt.Fprintln(w, " - ", target)
		}
	}
	return nil
}

// ruleOutput determines the io.Writers to receive the stderr and stdout output
// of a rule's recipe commands.
func (m *Maker) ruleOutput(r Rule) (stdout io.Writer, stderr io.Writer, logger *log.Logger) {
	if m.RuleOutput != nil {
		return m.RuleOutput(r)
	}
	return os.Stdout, os.Stderr, log.New(os.Stderr, fmt.Sprintf("%s: ", r.Target()), 0)
}

func (m *Maker) Run() error {
	targetSets, err := m.TargetSetsNeedingBuild()
	if err != nil {
		return err
	}

	for _, targetSet := range targetSets {
		par := parallel.NewRun(m.ParallelJobs)
		for _, target := range targetSet {
			rule := m.mf.Rule(target)
			stdout, stderr, log := m.ruleOutput(rule)
			par.Do(func() error {
				if m.Verbose {
					log.Printf("building...")
				}

				for _, recipe := range rule.Recipes() {
					recipe = ExpandAutoVars(rule, recipe)
					if m.Verbose {
						log.Printf("running command: %s", recipe)
					}
					cmd := exec.Command("sh", "-c", recipe)
					cmd.Stdout, cmd.Stderr = stdout, stderr

					err := cmd.Run()
					if err != nil {
						// remove files if failed
						if exists, _ := m.pathExists(rule.Target()); exists {
							err2 := m.fs().Remove(rule.Target())
							if err2 != nil {
								log.Printf("failed to remove target after error: %s", err)
							}
						}

						log.Printf(`command failed: %s (%s)`, recipe, err)
						return fmt.Errorf("command failed: %s (%s)", recipe, err)
					}
				}

				if m.Verbose {
					log.Printf("build completed")
				}

				return nil
			})
		}
		err := par.Wait()
		if err != nil {
			return Errors(err.(parallel.Errors))
		}
	}

	return nil
}

func errNoRuleToMakeTarget(target string) error {
	return fmt.Errorf("no rule to make target %q", target)
}

func errCircularDependency(target string, deps []string) error {
	return fmt.Errorf("circular dependency for target %q: %v", target, deps)
}
<commit_msg>channels for tracking progress<commit_after>package makex

import (
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"

	"code.google.com/p/rog-go/parallel"
)

// TargetsNeedingBuild returns an ordered list of target sets
func (c *Config) NewMaker(mf *Makefile, goals ...string) *Maker {
	m := &Maker{
		mf:     mf,
		goals:  goals,
		Config: c,
	}
	m.buildDAG()
	return m
}

type Maker struct {
	mf     *Makefile
	goals  []string
	dag    map[string][]string
	topo   [][]string
	cycles map[string][]string

	// RuleOutput specifies the writers to receive the stdout and stderr output
	// from executing a rule's recipes. If RuleOutput is nil, os.Stdout and
	// os.Stderr are used, respectively.
	RuleOutput func(Rule) (out io.Writer, err io.Writer, logger *log.Logger)

	// Channels to monitor progress. If non-nil, these channels are called at
	// various stages of building targets. Ended is always called *after*
	// Succeeded or Failed.
	Started, Ended, Succeeded chan<- Rule
	Failed                    chan<- RuleBuildError

	*Config
}

func (m *Maker) buildDAG() {
	// topological sort taken from
	// http://rosettacode.org/wiki/Topological_sort#Go.

	if m.dag == nil || m.cycles == nil {
		m.dag = make(map[string][]string)
		m.cycles = make(map[string][]string)
	}

	seen := make(map[string]struct{})
	queue := append([]string{}, m.goals...)
	for {
		if len(queue) == 0 {
			break
		}
		origLen := len(queue)
		for _, target := range queue {
			if _, seen := seen[target]; seen {
				continue
			}
			seen[target] = struct{}{}

			rule := m.mf.Rule(target)
			if rule == nil {
				continue
			}
			m.dag[target] = append([]string{}, rule.Prereqs()...)
			for _, dep := range rule.Prereqs() {
				// make a node for the prereq target even if it isn't defined
				queue = append(queue, dep)
				m.dag[dep] = m.dag[dep]
			}
		}
		queue = queue[origLen:]
	}

	// topological sort on the DAG
	for len(m.dag) > 0 {

		// collect targets with no dependencies
		var zero []string
		for target, prereqs := range m.dag {
			if len(prereqs) == 0 {
				zero = append(zero, target)
				delete(m.dag, target)
			}
		}

		// cycle detection
		if len(zero) == 0 {
			// collect un-orderable dependencies
			cycle := make(map[string]bool)
			for _, prereqs := range m.dag {
				for _, dep := range prereqs {
					cycle[dep] = true
				}
			}

			// mark targets with un-orderable dependencies
			for target, prereqs := range m.dag {
				if cycle[target] {
					m.cycles[target] = prereqs
				}
			}
			return
		}

		// output a set that can be processed concurrently
		m.topo = append(m.topo, zero)

		// remove edges (dependencies) from dg
		for _, remove := range zero {
			for target, prereqs := range m.dag {
				for i, dep := range prereqs {
					if dep == remove {
						copy(prereqs[i:], prereqs[i+1:])
						m.dag[target] = prereqs[:len(prereqs)-1]
						break
					}
				}
			}
		}
	}
}

func (m *Maker) TargetSets() [][]string {
	return m.topo
}

func (m *Maker) TargetSetsNeedingBuild() ([][]string, error) {
	for _, goal := range m.goals {
		if rule := m.mf.Rule(goal); rule == nil {
			return nil, errNoRuleToMakeTarget(goal)
		}
		if deps, isCycle := m.cycles[goal]; isCycle {
			return nil, errCircularDependency(goal, deps)
		}
	}

	targetSets := make([][]string, 0)
	for _, targetSet := range m.topo {
		var targetsNeedingBuild []string
		for _, target := range targetSet {
			exists, err := m.pathExists(target)
			if err != nil {
				return nil, err
			}
			if !exists {
				rule := m.mf.Rule(target)
				if rule == nil {
					return nil, errNoRuleToMakeTarget(target)
				}
				targetsNeedingBuild = append(targetsNeedingBuild, target)
			}
		}
		if len(targetsNeedingBuild) > 0 {
			targetSets = append(targetSets, targetsNeedingBuild)
		}
	}
	return targetSets, nil
}

// DryRun prints information about what targets *would* be built if Run() was
// called.
func (m *Maker) DryRun(w io.Writer) error {
	targetSets, err := m.TargetSetsNeedingBuild()
	if err != nil {
		return err
	}
	if len(targetSets) == 0 {
		fmt.Fprintln(w, "No target sets need building.")
	}
	for i, targetSet := range targetSets {
		if i != 0 {
			fmt.Fprintln(w)
		}
		fmt.Fprintf(w, "========= TARGET SET %d (%d targets)\n", i, len(targetSet))
		for _, target := range targetSet {
			fmt.Fprintln(w, " - ", target)
		}
	}
	return nil
}

// ruleOutput determines the io.Writers to receive the stderr and stdout output
// of a rule's recipe commands.
func (m *Maker) ruleOutput(r Rule) (stdout io.Writer, stderr io.Writer, logger *log.Logger) {
	if m.RuleOutput != nil {
		return m.RuleOutput(r)
	}
	return os.Stdout, os.Stderr, log.New(os.Stderr, fmt.Sprintf("%s: ", r.Target()), 0)
}

func (m *Maker) Run() error {
	targetSets, err := m.TargetSetsNeedingBuild()
	if err != nil {
		return err
	}

	for _, targetSet := range targetSets {
		par := parallel.NewRun(m.ParallelJobs)
		for _, target := range targetSet {
			rule := m.mf.Rule(target)
			stdout, stderr, log := m.ruleOutput(rule)
			par.Do(func() error {
				if m.Verbose {
					log.Printf("building...")
				}
				if m.Started != nil {
					m.Started <- rule
				}
				go func() {
					if m.Ended != nil {
						m.Ended <- rule
					}
				}()

				for _, recipe := range rule.Recipes() {
					recipe = ExpandAutoVars(rule, recipe)
					if m.Verbose {
						log.Printf("running command: %s", recipe)
					}
					cmd := exec.Command("sh", "-c", recipe)
					cmd.Stdout, cmd.Stderr = stdout, stderr

					err := cmd.Run()
					if err != nil {
						// remove files if failed
						if exists, _ := m.pathExists(rule.Target()); exists {
							err2 := m.fs().Remove(rule.Target())
							if err2 != nil {
								log.Printf("failed to remove target after error: %s", err)
							}
						}

						log.Printf(`command failed: %s (%s)`, recipe, err)
						err2 := RuleBuildError{rule, fmt.Errorf("command failed: %s (%s)", recipe, err)}
						if m.Failed != nil {
							m.Failed <- err2
						}
						return err2
					}
				}

				if m.Succeeded != nil {
					m.Succeeded <- rule
				}
				if m.Verbose {
					log.Printf("build completed")
				}

				return nil
			})
		}
		err := par.Wait()
		if err != nil {
			return Errors(err.(parallel.Errors))
		}
	}

	return nil
}

type RuleBuildError struct {
	Rule Rule
	Err  error
}

func (e RuleBuildError) Error() string { return e.Err.Error() }

func errNoRuleToMakeTarget(target string) error {
	return fmt.Errorf("no rule to make target %q", target)
}

func errCircularDependency(target string, deps []string) error {
	return fmt.Errorf("circular dependency for target %q: %v", target, deps)
}
<|endoftext|>


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

// DO NOT EDIT. THIS IS AUTOMATICALLY GENERATED.
// Run "go generate" to regenerate.
//go:generate go run cbt.go -o cbtdoc.go doc

/*
Cbt is a tool for doing basic interactions with Cloud Bigtable. To learn how to
install the cbt tool, see the
[cbt overview](https://cloud.google.com/bigtable/docs/go/cbt-overview).

Usage:

	cbt [options] command [arguments]

The commands are:

	count                     Count rows in a table
	createinstance            Create an instance with an initial cluster
	createcluster             Create a cluster in the configured instance (replication alpha)
	createfamily              Create a column family
	createtable               Create a table
	updatecluster             Update a cluster in the configured instance
	deleteinstance            Deletes an instance
	deletecluster             Deletes a cluster from the configured instance (replication alpha)
	deletecolumn              Delete all cells in a column
	deletefamily              Delete a column family
	deleterow                 Delete a row
	deletetable               Delete a table
	doc                       Print godoc-suitable documentation for cbt
	help                      Print help text
	listinstances             List instances in a project
	listclusters              List instances in an instance
	lookup                    Read from a single row
	ls                        List tables and column families
	mddoc                     Print documentation for cbt in Markdown format
	read                      Read rows
	set                       Set value of a cell
	setgcpolicy               Set the GC policy for a column family
	waitforreplication        Blocks until all the completed writes have been replicated to all the clusters (replication alpha)
	version                   Print the current cbt version

Use "cbt help <command>" for more information about a command.

The options are:

	-project string
		project ID, if unset uses gcloud configured project
	-instance string
		Cloud Bigtable instance
	-creds string
		if set, use application credentials in this file

Alpha features are not currently available to most Cloud Bigtable customers. The
features might be changed in backward-incompatible ways and are not recommended
for production use. They are not subject to any SLA or deprecation policy.

For convenience, values of the -project, -instance, -creds,
-admin-endpoint and -data-endpoint flags may be specified in
/usr/local/google/home/igorbernstein/.cbtrc in this format:
	project = my-project-123
	instance = my-instance
	creds = path-to-account-key.json
	admin-endpoint = hostname:port
	data-endpoint = hostname:port
All values are optional, and all will be overridden by flags.

Count rows in a table

Usage:
	cbt count

Create an instance with an initial cluster

Usage:
	cbt createinstance <instance-id> <display-name> <cluster-id> <zone> <num-nodes> <storage type>
	  instance-id					Permanent, unique id for the instance
	  display-name	  			Description of the instance
	  cluster-id						Permanent, unique id for the cluster in the instance
	  zone				  				The zone in which to create the cluster
	  num-nodes	  				The number of nodes to create
	  storage-type					SSD or HDD

Create a cluster in the configured instance (replication alpha)

Usage:
	cbt createcluster <cluster-id> <zone> <num-nodes> <storage type>
	  cluster-id		Permanent, unique id for the cluster in the instance
	  zone				  The zone in which to create the cluster
	  num-nodes	  The number of nodes to create
	  storage-type	SSD or HDD

Create a column family

Usage:
	cbt createfamily
 <family>

Create a table

Usage:
	cbt createtable
 [families=family[:(maxage=<d> | maxversions=<n>)],...] [splits=split,...]
	  families: Column families and their associated GC policies. See "setgcpolicy".
	  					 Example: families=family1:maxage=1w,family2:maxversions=1
	  splits:   Row key to be used to initially split the table

Update a cluster in the configured instance

Usage:
	cbt updatecluster <cluster-id> [num-nodes=num-nodes]
	  cluster-id		Permanent, unique id for the cluster in the instance
	  num-nodes		The number of nodes to update to

Deletes an instance

Usage:
	cbt deleteinstance <instance>

Deletes a cluster from the configured instance (replication alpha)

Usage:
	cbt deletecluster <cluster>

Delete all cells in a column

Usage:
	cbt deletecolumn
 <row> <family> <column> [app-profile=<app profile id>]
	  app-profile=<app profile id>		The app profile id to use for the request (replication alpha)

Delete a column family

Usage:
	cbt deletefamily
 <family>

Delete a row

Usage:
	cbt deleterow
 <row> [app-profile=<app profile id>]
	  app-profile=<app profile id>		The app profile id to use for the request (replication alpha)

Delete a table

Usage:
	cbt deletetable

Print godoc-suitable documentation for cbt

Usage:
	cbt doc

Print help text

Usage:
	cbt help [command]

List instances in a project

Usage:
	cbt listinstances

List instances in an instance

Usage:
	cbt listclusters

Read from a single row

Usage:
	cbt lookup
 <row> [app-profile=<app profile id>]
	  app-profile=<app profile id>		The app profile id to use for the request (replication alpha)

List tables and column families

Usage:
	cbt ls			List tables
	cbt ls
		List column families in

Print documentation for cbt in Markdown format

Usage:
	cbt mddoc

Read rows

Usage:
	cbt read
 [start=<row>] [end=<row>] [prefix=<prefix>] [regex=<regex>] [count=<n>] [app-profile=<app profile id>]
	  start=<row>		Start reading at this row
	  end=<row>		Stop reading before this row
	  prefix=<prefix>	Read rows with this prefix
	  regex=<regex> 	Read rows with keys matching this regex
	  count=<n>		Read only this many rows
	  app-profile=<app profile id>		The app profile id to use for the request (replication alpha)

Set value of a cell

Usage:
	cbt set
 <row> [app-profile=<app profile id>] family:column=val[@ts] ...
	  app-profile=<app profile id>		The app profile id to use for the request (replication alpha)
	  family:column=val[@ts] may be repeated to set multiple cells.

	  ts is an optional integer timestamp.
	  If it cannot be parsed, the `@ts` part will be
	  interpreted as part of the value.

Set the GC policy for a column family

Usage:
	cbt setgcpolicy
 <family> ( maxage=<d> | maxversions=<n> )

	  maxage=<d>		Maximum timestamp age to preserve (e.g. "1h", "4d")
	  maxversions=<n>	Maximum number of versions to preserve

Blocks until all the completed writes have been replicated to all the clusters (replication alpha)

Usage:
	cbt waitforreplication

Print the current cbt version

Usage:
	cbt version

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

// DO NOT EDIT. THIS IS AUTOMATICALLY GENERATED.
// Run "go generate" to regenerate.
//go:generate go run cbt.go -o cbtdoc.go doc

/*
Cbt is a tool for doing basic interactions with Cloud Bigtable. To learn how to
install the cbt tool, see the
[cbt overview](https://cloud.google.com/bigtable/docs/go/cbt-overview).

Usage:

	cbt [options] command [arguments]

The commands are:

	count                     Count rows in a table
	createinstance            Create an instance with an initial cluster
	createcluster             Create a cluster in the configured instance (replication alpha)
	createfamily              Create a column family
	createtable               Create a table
	updatecluster             Update a cluster in the configured instance
	deleteinstance            Deletes an instance
	deletecluster             Deletes a cluster from the configured instance (replication alpha)
	deletecolumn              Delete all cells in a column
	deletefamily              Delete a column family
	deleterow                 Delete a row
	deletetable               Delete a table
	doc                       Print godoc-suitable documentation for cbt
	help                      Print help text
	listinstances             List instances in a project
	listclusters              List instances in an instance
	lookup                    Read from a single row
	ls                        List tables and column families
	mddoc                     Print documentation for cbt in Markdown format
	read                      Read rows
	set                       Set value of a cell
	setgcpolicy               Set the GC policy for a column family
	waitforreplication        Blocks until all the completed writes have been replicated to all the clusters (replication alpha)
	createtablefromsnapshot   Create a table from a snapshot (snapshots alpha)
	createsnapshot            Create a snapshot from a source table (snapshots alpha)
	listsnapshots             List snapshots in a cluster (snapshots alpha)
	getsnapshot               Get snapshot info (snapshots alpha)
	deletesnapshot            Delete snapshot in a cluster (snapshots alpha)
	version                   Print the current cbt version

Use "cbt help <command>" for more information about a command.

The options are:

	-project string
		project ID, if unset uses gcloud configured project
	-instance string
		Cloud Bigtable instance
	-creds string
		if set, use application credentials in this file

Alpha features are not currently available to most Cloud Bigtable customers. The
features might be changed in backward-incompatible ways and are not recommended
for production use. They are not subject to any SLA or deprecation policy.

For convenience, values of the -project, -instance, -creds,
-admin-endpoint and -data-endpoint flags may be specified in
.cbtrc in this format:
	project = my-project-123
	instance = my-instance
	creds = path-to-account-key.json
	admin-endpoint = hostname:port
	data-endpoint = hostname:port
All values are optional, and all will be overridden by flags.

Count rows in a table

Usage:
	cbt count

Create an instance with an initial cluster

Usage:
	cbt createinstance <instance-id> <display-name> <cluster-id> <zone> <num-nodes> <storage type>
	  instance-id					Permanent, unique id for the instance
	  display-name	  			Description of the instance
	  cluster-id						Permanent, unique id for the cluster in the instance
	  zone				  				The zone in which to create the cluster
	  num-nodes	  				The number of nodes to create
	  storage-type					SSD or HDD

Create a cluster in the configured instance (replication alpha)

Usage:
	cbt createcluster <cluster-id> <zone> <num-nodes> <storage type>
	  cluster-id		Permanent, unique id for the cluster in the instance
	  zone				  The zone in which to create the cluster
	  num-nodes	  The number of nodes to create
	  storage-type	SSD or HDD

Create a column family

Usage:
	cbt createfamily
 <family>

Create a table

Usage:
	cbt createtable
 [families=family[:(maxage=<d> | maxversions=<n>)],...] [splits=split,...]
	  families: Column families and their associated GC policies. See "setgcpolicy".
	  					 Example: families=family1:maxage=1w,family2:maxversions=1
	  splits:   Row key to be used to initially split the table

Update a cluster in the configured instance

Usage:
	cbt updatecluster <cluster-id> [num-nodes=num-nodes]
	  cluster-id		Permanent, unique id for the cluster in the instance
	  num-nodes		The number of nodes to update to

Deletes an instance

Usage:
	cbt deleteinstance <instance>

Deletes a cluster from the configured instance (replication alpha)

Usage:
	cbt deletecluster <cluster>

Delete all cells in a column

Usage:
	cbt deletecolumn
 <row> <family> <column> [app-profile=<app profile id>]
	  app-profile=<app profile id>		The app profile id to use for the request (replication alpha)

Delete a column family

Usage:
	cbt deletefamily
 <family>

Delete a row

Usage:
	cbt deleterow
 <row> [app-profile=<app profile id>]
	  app-profile=<app profile id>		The app profile id to use for the request (replication alpha)

Delete a table

Usage:
	cbt deletetable

Print godoc-suitable documentation for cbt

Usage:
	cbt doc

Print help text

Usage:
	cbt help [command]

List instances in a project

Usage:
	cbt listinstances

List instances in an instance

Usage:
	cbt listclusters

Read from a single row

Usage:
	cbt lookup
 <row> [cells-per-column=<n>] [app-profile=<app profile id>]
	  cells-per-column=<n> 			Read only this many cells per column
	  app-profile=<app profile id>		The app profile id to use for the request (replication alpha)

List tables and column families

Usage:
	cbt ls			List tables
	cbt ls
		List column families in

Print documentation for cbt in Markdown format

Usage:
	cbt mddoc

Read rows

Usage:
	cbt read
 [start=<row>] [end=<row>] [prefix=<prefix>] [regex=<regex>] [count=<n>] [cells-per-column=<n>] [app-profile=<app profile id>]
	  start=<row>		Start reading at this row
	  end=<row>		Stop reading before this row
	  prefix=<prefix>	Read rows with this prefix
	  regex=<regex> 	Read rows with keys matching this regex
	  count=<n>		Read only this many rows
	  cells-per-column=<n>	Read only this many cells per column
	  app-profile=<app profile id>		The app profile id to use for the request (replication alpha)

Set value of a cell

Usage:
	cbt set
 <row> [app-profile=<app profile id>] family:column=val[@ts] ...
	  app-profile=<app profile id>		The app profile id to use for the request (replication alpha)
	  family:column=val[@ts] may be repeated to set multiple cells.

	  ts is an optional integer timestamp.
	  If it cannot be parsed, the `@ts` part will be
	  interpreted as part of the value.

Set the GC policy for a column family

Usage:
	cbt setgcpolicy
 <family> ( maxage=<d> | maxversions=<n> )

	  maxage=<d>		Maximum timestamp age to preserve (e.g. "1h", "4d")
	  maxversions=<n>	Maximum number of versions to preserve

Blocks until all the completed writes have been replicated to all the clusters (replication alpha)

Usage:
	cbt waitforreplication

Create a table from a snapshot (snapshots alpha)

Usage:
	cbt createtablefromsnapshot
 <cluster> <snapshot>
	  table	The name of the table to create
	  cluster	The cluster where the snapshot is located
	  snapshot	The snapshot to restore

Create a snapshot from a source table (snapshots alpha)

Usage:
	cbt createsnapshot <cluster> <snapshot>
 [ttl=<d>]

	  [ttl=<d>]		Lifespan of the snapshot (e.g. "1h", "4d")

List snapshots in a cluster (snapshots alpha)

Usage:
	cbt listsnapshots [<cluster>]

Get snapshot info (snapshots alpha)

Usage:
	cbt getsnapshot <cluster> <snapshot>

Delete snapshot in a cluster (snapshots alpha)

Usage:
	cbt deletesnapshot <cluster> <snapshot>

Print the current cbt version

Usage:
	cbt version

*/
package main
<|endoftext|>


<commit_before>package floats

import (
	"math"
	"math/big"
)

// agm returns the arithmetic-geometric mean of a and b, to
// max(a.Prec, b.Prec) bits of precision. a and b must have
// the same precision.
func agm(a, b *big.Float) *big.Float {

	if a.Prec() != b.Prec() {
		panic("agm: different precisions")
	}

	var prec uint = a.Prec() + 64

	half := new(big.Float).SetPrec(prec).SetFloat64(0.5)

	a2 := new(big.Float)
	b2 := new(big.Float)
	a2.Copy(a).SetPrec(prec)
	b2.Copy(b).SetPrec(prec)

	checkA := new(big.Float).Copy(a2).SetPrec(prec - 64)
	checkB := new(big.Float).Copy(b2).SetPrec(prec - 64)
	t1 := new(big.Float)

	// iterate until a2 == b2 (with a2 and b2 reduced to the
	// desired precision, i.e. ignoring the guard digits)
	for checkA.Cmp(checkB) != 0 {
		t1.Copy(a2)
		a2.Add(a2, b2).Mul(a2, half)
		b2 = Sqrt(b2.Mul(t1, b2))

		checkA.Copy(a2).SetPrec(prec - 64)
		checkB.Copy(b2).SetPrec(prec - 64)
	}

	return checkA
}

// pi returns pi to prec bits of precision
func pi(prec uint) *big.Float {

	precExt := prec + 64

	half := new(big.Float).SetPrec(precExt).SetFloat64(0.5)
	one := new(big.Float).SetPrec(precExt).SetInt64(1)
	two := new(big.Float).SetPrec(precExt).SetInt64(2)
	four := new(big.Float).SetPrec(precExt).SetInt64(4)
	temp := new(big.Float)

	a := new(big.Float).SetPrec(precExt).SetInt64(1)      // a_0 = 1
	b := new(big.Float).Quo(one, Sqrt(two))               // b_0 = 1/sqrt(2)
	t := new(big.Float).SetPrec(precExt).SetFloat64(0.25) // t_0 = 1/4
	p := new(big.Float).SetPrec(precExt).SetInt64(1)      // p_0 = 1

	steps := math.Log2(float64(precExt))

	a2 := new(big.Float)
	for i := 0; i < int(steps); i++ {
		a2.Add(a, b).Mul(a2, half) // a_{n+1} = (a_{n} + b_{n}) / 2
		b = Sqrt(temp.Mul(a, b))   // b_{n+1} = sqrt(a_{n} * b_{n})

		temp.Sub(a, a2).Mul(temp, temp) // temp = (a_{n} - a_{n+1})²
		t.Sub(t, temp.Mul(p, temp))     // t_{n+1} = t_{n} - p_{n} * temp

		p.Mul(two, p) // p_{n+1} = 2 * p_{n}

		a.Copy(a2)
	}

	temp.Add(a, b)
	temp.Mul(temp, temp)
	res := new(big.Float).Quo(temp, t.Mul(four, t)) // pi = (a_{n+1} + b_{n+1})² / (4t_{n+1})

	return res.SetPrec(prec)
}
<commit_msg>misc.go: cache the first 1064 bits of pi<commit_after>package floats

import (
	"math"
	"math/big"
)

// agm returns the arithmetic-geometric mean of a and b, to
// max(a.Prec, b.Prec) bits of precision. a and b must have
// the same precision.
func agm(a, b *big.Float) *big.Float {

	if a.Prec() != b.Prec() {
		panic("agm: different precisions")
	}

	var prec uint = a.Prec() + 64

	half := new(big.Float).SetPrec(prec).SetFloat64(0.5)

	a2 := new(big.Float)
	b2 := new(big.Float)
	a2.Copy(a).SetPrec(prec)
	b2.Copy(b).SetPrec(prec)

	checkA := new(big.Float).Copy(a2).SetPrec(prec - 64)
	checkB := new(big.Float).Copy(b2).SetPrec(prec - 64)
	t1 := new(big.Float)

	// iterate until a2 == b2 (with a2 and b2 reduced to the
	// desired precision, i.e. ignoring the guard digits)
	for checkA.Cmp(checkB) != 0 {
		t1.Copy(a2)
		a2.Add(a2, b2).Mul(a2, half)
		b2 = Sqrt(b2.Mul(t1, b2))

		checkA.Copy(a2).SetPrec(prec - 64)
		checkB.Copy(b2).SetPrec(prec - 64)
	}

	return checkA
}

// Parse and cache pi to 1064 binary digits.
var piCache *big.Float

func init() {
	piCache, _, _ = new(big.Float).SetPrec(1e4+64).Parse("3."+
		"14159265358979323846264338327950288419716939937510"+
		"58209749445923078164062862089986280348253421170679"+
		"82148086513282306647093844609550582231725359408128"+
		"48111745028410270193852110555964462294895493038196"+
		"44288109756659334461284756482337867831652712019091"+
		"45648566923460348610454326648213393607260249141273"+
		"72458700660631558817488152092096282925409171536444", 10)
}

// pi returns pi to prec bits of precision
func pi(prec uint) *big.Float {

	if prec <= 1064 {
		return new(big.Float).Copy(piCache).SetPrec(prec)
	}

	precExt := prec + 64

	half := new(big.Float).SetPrec(precExt).SetFloat64(0.5)
	one := new(big.Float).SetPrec(precExt).SetInt64(1)
	two := new(big.Float).SetPrec(precExt).SetInt64(2)
	four := new(big.Float).SetPrec(precExt).SetInt64(4)
	temp := new(big.Float)

	a := new(big.Float).SetPrec(precExt).SetInt64(1)      // a_0 = 1
	b := new(big.Float).Quo(one, Sqrt(two))               // b_0 = 1/sqrt(2)
	t := new(big.Float).SetPrec(precExt).SetFloat64(0.25) // t_0 = 1/4
	p := new(big.Float).SetPrec(precExt).SetInt64(1)      // p_0 = 1

	steps := math.Log2(float64(precExt))

	a2 := new(big.Float)
	for i := 0; i < int(steps); i++ {
		a2.Add(a, b).Mul(a2, half) // a_{n+1} = (a_{n} + b_{n}) / 2
		b = Sqrt(temp.Mul(a, b))   // b_{n+1} = sqrt(a_{n} * b_{n})

		temp.Sub(a, a2).Mul(temp, temp) // temp = (a_{n} - a_{n+1})²
		t.Sub(t, temp.Mul(p, temp))     // t_{n+1} = t_{n} - p_{n} * temp

		p.Mul(two, p) // p_{n+1} = 2 * p_{n}

		a.Copy(a2)
	}

	temp.Add(a, b)
	temp.Mul(temp, temp)
	res := new(big.Float).Quo(temp, t.Mul(four, t)) // pi = (a_{n+1} + b_{n+1})² / (4t_{n+1})

	return res.SetPrec(prec)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"regexp"
	"sort"
	"strconv"
	"strings"

	statsd "github.com/DataDog/datadog-go/statsd"
	log "github.com/Sirupsen/logrus"
)

const sampleRate = 1.0

const (
	routerMsg int = iota
	scalingMsg
	sampleMsg
	metricsTag
	releaseMsg
)

var routerMetricsKeys = []string{"dyno", "method", "status", "path", "host", "code", "desc", "at"}
var sampleMetricsKeys = []string{"source"}
var scalingMetricsKeys = []string{"mailer", "web"}
var customMetricsKeys = []string{"media_type", "output_type", "path", "version", "name"}

type Client struct {
	*statsd.Client
	ExcludedTags map[string]bool
}

var statusCode *regexp.Regexp = regexp.MustCompile(`^(?P<Family>\d)\d\d`)

func statsdClient(addr string) (*Client, error) {

	c, err := statsd.New(addr)
	return &Client{c, make(map[string]bool)}, err
}

func (c *Client) sendToStatsd(in chan *logMetrics) {

	var data *logMetrics
	var ok bool
	for {
		data, ok = <-in

		if !ok { //Exit, channel was closed
			return
		}

		log.WithFields(log.Fields{
			"type":   data.typ,
			"app":    data.app,
			"tags":   data.tags,
			"prefix": data.prefix,
		}).Debug("logMetrics received")

		if data.typ == routerMsg {
			c.sendRouterMsg(data)
		} else if data.typ == sampleMsg {
			c.sendSampleMsg(data)
		} else if data.typ == scalingMsg {
			c.sendEvents(*data.app, "heroku", data.events, *data.tags)
			c.sendScalingMsg(data)
		} else if data.typ == metricsTag {
			c.sendMetricsWithTags(data)
		} else if data.typ == releaseMsg {
			c.sendEvents(*data.app, "app", data.events, *data.tags)
		} else {
			log.WithField("type", data.typ).Warn("Unknown log message")
		}
	}
}

func (c *Client) sendEvents(app string, namespace string, events []string, tags []string) {
	for _, v := range events {
		event := statsd.NewEvent(namespace+"/api: "+app, v)
		event.Tags = tags
		c.Event(event)
		log.WithFields(log.Fields{
			"type":  "event",
			"app":   app,
			"value": v,
		}).Info("Event sent")
	}
}

func (c *Client) extractTags(tags []string, permittedTags []string, metrics map[string]logValue) []string {
	for _, mk := range permittedTags {
		if c.ExcludedTags[mk] {
			continue
		}
		if v, ok := metrics[mk]; ok {
			tags = append(tags, mk+":"+v.Val)
		}
	}
	sort.Strings(tags)
	return tags
}

func addStatusFamilyToTags(data *logMetrics, tags []string) []string {
	if val, ok := data.metrics["status"]; ok {
		match := statusCode.FindStringSubmatch(val.Val)
		if len(match) > 1 {
			tags = append(tags, "statusFamily:"+match[1]+"xx")
		}
	}
	return tags
}

func (c *Client) sendRouterMsg(data *logMetrics) {
	tags := c.extractTags(*data.tags, routerMetricsKeys, data.metrics)
	tags = addStatusFamilyToTags(data, tags)

	log.WithFields(log.Fields{
		"app":    *data.app,
		"tags":   *data.tags,
		"prefix": *data.prefix,
	}).Debug("sendRouterMsg")

	conn, err := strconv.ParseFloat(data.metrics["connect"].Val, 10)
	if err != nil {
		log.WithFields(log.Fields{
			"type":   "router",
			"err":    err,
			"metric": "connect",
		}).Info("Could not parse metric value")
		return
	}
	serv, err := strconv.ParseFloat(data.metrics["service"].Val, 10)
	if err != nil {
		log.WithFields(log.Fields{
			"type":   "router",
			"metric": "service",
			"err":    err,
		}).Info("Could not parse metric value")
		return
	}

	err = c.Histogram(*data.prefix+"heroku.router.request.connect", conn, tags, sampleRate)
	if err != nil {
		log.WithField("error", err).Info("Failed to send Histogram")
	}
	err = c.Histogram(*data.prefix+"heroku.router.request.service", serv, tags, sampleRate)
	if err != nil {
		log.WithField("error", err).Info("Failed to send Histogram")
	}
	if data.metrics["at"].Val == "error" {
		err = c.Count(*data.prefix+"heroku.router.error", 1, tags, 0.1)
		if err != nil {
			log.WithField("error", err).Info("Failed to send Count")
		}
	}
}

func (c *Client) sendSampleMsg(data *logMetrics) {
	tags := c.extractTags(*data.tags, sampleMetricsKeys, data.metrics)

	log.WithFields(log.Fields{
		"app":    *data.app,
		"tags":   tags,
		"prefix": *data.prefix,
	}).Debug("sendSampleMsg")

	for k, v := range data.metrics {
		if strings.Index(k, "#") != -1 {
			m := strings.Replace(strings.Split(k, "#")[1], "_", ".", -1)
			vnum, err := strconv.ParseFloat(v.Val, 10)
			if err == nil {
				err = c.Gauge(*data.prefix+"heroku.dyno."+m, vnum, tags, sampleRate)
				if err != nil {
					log.WithField("error", err).Info("Failed to send Gauge")
				}
			} else {
				log.WithFields(log.Fields{
					"type":   "sample",
					"metric": k,
					"err":    err,
				}).Info("Could not parse metric value")
			}
		}
	}
}

func (c *Client) sendScalingMsg(data *logMetrics) {
	tags := *data.tags

	log.WithFields(log.Fields{
		"app":    *data.app,
		"tags":   tags,
		"prefix": *data.prefix,
	}).Debug("sendScalingMsg")

	for _, mk := range scalingMetricsKeys {
		if v, ok := data.metrics[mk]; ok {
			vnum, err := strconv.ParseFloat(v.Val, 10)
			if err == nil {
				ntags := append(tags, "type:"+mk)
				err = c.Gauge(*data.prefix+"heroku.dyno.scale", vnum, ntags, sampleRate)
				if err != nil {
					log.WithField("error", err).Info("Failed to send Gauge")
				}
			} else {
				log.WithFields(log.Fields{
					"type":   "scaling",
					"metric": mk,
					"err":    err,
				}).Info("Could not parse metric value")
			}
		}
	}
}

func (c *Client) sendMetric(metricType string, metricName string, value float64, tags []string) error {
	switch metricType {
	case "sample":
		return c.Gauge(metricName, value, tags, sampleRate)
	case "measure":
		return c.Histogram(metricName, value, tags, sampleRate)
	case "metric", "count":
		return c.Count(metricName, int64(value), tags, sampleRate)
	default:
		return errors.New("Unknown metric type" + metricType)
	}
}

func (c *Client) sendMetricsWithTags(data *logMetrics) {
	tags := *data.tags

Tags:
	for k, v := range data.metrics {
		if strings.Index(k, "tag#") != -1 {
			if _, err := strconv.Atoi(v.Val); err != nil {
				m := strings.Replace(strings.Split(k, "tag#")[1], "_", ".", -1)
				for _, mk := range customMetricsKeys {
					if m == mk {
						tags = append(tags, mk+":"+v.Val)
						continue Tags
					}
				}
			}
		}
	}

	log.WithFields(log.Fields{
		"app":    *data.app,
		"tags":   tags,
		"prefix": *data.prefix,
	}).Debug("sendMetricTag")

	for k, v := range data.metrics {
		if strings.Index(k, "#") != -1 {
			if vnum, err := strconv.ParseFloat(v.Val, 10); err == nil {
				keySplit := strings.Split(k, "#")
				metricType := keySplit[0]
				m := strings.Replace(keySplit[1], "_", ".", -1)
				err = c.sendMetric(metricType, *data.prefix+m, vnum, tags)
				if err != nil {
					log.WithField("error", err).Warning("Failed to send Gauge")
				}
			} else {
				log.WithFields(log.Fields{
					"type":   "metrics",
					"metric": k,
					"err":    err,
				}).Debug("Could not parse metric value")
			}
		}
	}
}
<commit_msg>Allow all tags on custom metrics<commit_after>package main

import (
	"errors"
	"regexp"
	"sort"
	"strconv"
	"strings"

	statsd "github.com/DataDog/datadog-go/statsd"
	log "github.com/Sirupsen/logrus"
)

const sampleRate = 1.0

const (
	routerMsg int = iota
	scalingMsg
	sampleMsg
	metricsTag
	releaseMsg
)

var routerMetricsKeys = []string{"dyno", "method", "status", "path", "host", "code", "desc", "at"}
var sampleMetricsKeys = []string{"source"}
var scalingMetricsKeys = []string{"mailer", "web"}

type Client struct {
	*statsd.Client
	ExcludedTags map[string]bool
}

var statusCode *regexp.Regexp = regexp.MustCompile(`^(?P<Family>\d)\d\d`)

func statsdClient(addr string) (*Client, error) {

	c, err := statsd.New(addr)
	return &Client{c, make(map[string]bool)}, err
}

func (c *Client) sendToStatsd(in chan *logMetrics) {

	var data *logMetrics
	var ok bool
	for {
		data, ok = <-in

		if !ok { //Exit, channel was closed
			return
		}

		log.WithFields(log.Fields{
			"type":   data.typ,
			"app":    data.app,
			"tags":   data.tags,
			"prefix": data.prefix,
		}).Debug("logMetrics received")

		if data.typ == routerMsg {
			c.sendRouterMsg(data)
		} else if data.typ == sampleMsg {
			c.sendSampleMsg(data)
		} else if data.typ == scalingMsg {
			c.sendEvents(*data.app, "heroku", data.events, *data.tags)
			c.sendScalingMsg(data)
		} else if data.typ == metricsTag {
			c.sendMetricsWithTags(data)
		} else if data.typ == releaseMsg {
			c.sendEvents(*data.app, "app", data.events, *data.tags)
		} else {
			log.WithField("type", data.typ).Warn("Unknown log message")
		}
	}
}

func (c *Client) sendEvents(app string, namespace string, events []string, tags []string) {
	for _, v := range events {
		event := statsd.NewEvent(namespace+"/api: "+app, v)
		event.Tags = tags
		c.Event(event)
		log.WithFields(log.Fields{
			"type":  "event",
			"app":   app,
			"value": v,
		}).Info("Event sent")
	}
}

func (c *Client) extractTags(tags []string, permittedTags []string, metrics map[string]logValue) []string {
	for _, mk := range permittedTags {
		if c.ExcludedTags[mk] {
			continue
		}
		if v, ok := metrics[mk]; ok {
			tags = append(tags, mk+":"+v.Val)
		}
	}
	sort.Strings(tags)
	return tags
}

func addStatusFamilyToTags(data *logMetrics, tags []string) []string {
	if val, ok := data.metrics["status"]; ok {
		match := statusCode.FindStringSubmatch(val.Val)
		if len(match) > 1 {
			tags = append(tags, "statusFamily:"+match[1]+"xx")
		}
	}
	return tags
}

func (c *Client) sendRouterMsg(data *logMetrics) {
	tags := c.extractTags(*data.tags, routerMetricsKeys, data.metrics)
	tags = addStatusFamilyToTags(data, tags)

	log.WithFields(log.Fields{
		"app":    *data.app,
		"tags":   *data.tags,
		"prefix": *data.prefix,
	}).Debug("sendRouterMsg")

	conn, err := strconv.ParseFloat(data.metrics["connect"].Val, 10)
	if err != nil {
		log.WithFields(log.Fields{
			"type":   "router",
			"err":    err,
			"metric": "connect",
		}).Info("Could not parse metric value")
		return
	}
	serv, err := strconv.ParseFloat(data.metrics["service"].Val, 10)
	if err != nil {
		log.WithFields(log.Fields{
			"type":   "router",
			"metric": "service",
			"err":    err,
		}).Info("Could not parse metric value")
		return
	}

	err = c.Histogram(*data.prefix+"heroku.router.request.connect", conn, tags, sampleRate)
	if err != nil {
		log.WithField("error", err).Info("Failed to send Histogram")
	}
	err = c.Histogram(*data.prefix+"heroku.router.request.service", serv, tags, sampleRate)
	if err != nil {
		log.WithField("error", err).Info("Failed to send Histogram")
	}
	if data.metrics["at"].Val == "error" {
		err = c.Count(*data.prefix+"heroku.router.error", 1, tags, 0.1)
		if err != nil {
			log.WithField("error", err).Info("Failed to send Count")
		}
	}
}

func (c *Client) sendSampleMsg(data *logMetrics) {
	tags := c.extractTags(*data.tags, sampleMetricsKeys, data.metrics)

	log.WithFields(log.Fields{
		"app":    *data.app,
		"tags":   tags,
		"prefix": *data.prefix,
	}).Debug("sendSampleMsg")

	for k, v := range data.metrics {
		if strings.Index(k, "#") != -1 {
			m := strings.Replace(strings.Split(k, "#")[1], "_", ".", -1)
			vnum, err := strconv.ParseFloat(v.Val, 10)
			if err == nil {
				err = c.Gauge(*data.prefix+"heroku.dyno."+m, vnum, tags, sampleRate)
				if err != nil {
					log.WithField("error", err).Info("Failed to send Gauge")
				}
			} else {
				log.WithFields(log.Fields{
					"type":   "sample",
					"metric": k,
					"err":    err,
				}).Info("Could not parse metric value")
			}
		}
	}
}

func (c *Client) sendScalingMsg(data *logMetrics) {
	tags := *data.tags

	log.WithFields(log.Fields{
		"app":    *data.app,
		"tags":   tags,
		"prefix": *data.prefix,
	}).Debug("sendScalingMsg")

	for _, mk := range scalingMetricsKeys {
		if v, ok := data.metrics[mk]; ok {
			vnum, err := strconv.ParseFloat(v.Val, 10)
			if err == nil {
				ntags := append(tags, "type:"+mk)
				err = c.Gauge(*data.prefix+"heroku.dyno.scale", vnum, ntags, sampleRate)
				if err != nil {
					log.WithField("error", err).Info("Failed to send Gauge")
				}
			} else {
				log.WithFields(log.Fields{
					"type":   "scaling",
					"metric": mk,
					"err":    err,
				}).Info("Could not parse metric value")
			}
		}
	}
}

func (c *Client) sendMetric(metricType string, metricName string, value float64, tags []string) error {
	switch metricType {
	case "sample":
		return c.Gauge(metricName, value, tags, sampleRate)
	case "measure":
		return c.Histogram(metricName, value, tags, sampleRate)
	case "metric", "count":
		return c.Count(metricName, int64(value), tags, sampleRate)
	default:
		return errors.New("Unknown metric type" + metricType)
	}
}

func (c *Client) sendMetricsWithTags(data *logMetrics) {
	tags := *data.tags

Tags:
	for k, v := range data.metrics {
		if strings.Index(k, "tag#") != -1 {
			if _, err := strconv.Atoi(v.Val); err != nil {
				m := strings.Replace(strings.Split(k, "tag#")[1], "_", ".", -1)
				tags = append(tags, m+":"+v.Val)
				continue Tags
			}
		}
	}

	log.WithFields(log.Fields{
		"app":    *data.app,
		"tags":   tags,
		"prefix": *data.prefix,
	}).Debug("sendMetricTag")

	for k, v := range data.metrics {
		if strings.Index(k, "#") != -1 {
			if vnum, err := strconv.ParseFloat(v.Val, 10); err == nil {
				keySplit := strings.Split(k, "#")
				metricType := keySplit[0]
				m := strings.Replace(keySplit[1], "_", ".", -1)
				err = c.sendMetric(metricType, *data.prefix+m, vnum, tags)
				if err != nil {
					log.WithField("error", err).Warning("Failed to send Gauge")
				}
			} else {
				log.WithFields(log.Fields{
					"type":   "metrics",
					"metric": k,
					"err":    err,
				}).Debug("Could not parse metric value")
			}
		}
	}
}
<|endoftext|>


<commit_before>package ndp

import (
	"fmt"
	"net"

	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv6"
)

// A Conn is a Neighbor Discovery Protocol connection.
type Conn struct {
	pc *ipv6.PacketConn
	cm *ipv6.ControlMessage

	allNodes *net.IPAddr
}

// Dial dials a NDP connection using the specified interface.  It returns
// a Conn and the link-local IPv6 address of the interface.
func Dial(ifi *net.Interface) (*Conn, net.IP, error) {
	llAddr, err := linkLocalAddr(ifi)
	if err != nil {
		return nil, nil, err
	}

	ic, err := icmp.ListenPacket("ip6:ipv6-icmp", llAddr.String())
	if err != nil {
		return nil, nil, err
	}

	// Join the "all nodes" multicast group for this interface.
	allNodes := &net.IPAddr{
		IP:   net.IPv6linklocalallnodes,
		Zone: ifi.Name,
	}

	pc := ic.IPv6PacketConn()
	if err := pc.JoinGroup(ifi, allNodes); err != nil {
		return nil, nil, err
	}

	// Calculate and place ICMPv6 checksum at correct offset in all messages.
	if err := pc.SetChecksum(true, 2); err != nil {
		return nil, nil, err
	}

	c := &Conn{
		pc: pc,

		// The default control message used when none is specified.
		cm: &ipv6.ControlMessage{
			// Note that hop limit is always 255 in NDP.
			HopLimit: 255,
			Src:      llAddr.IP,
			IfIndex:  ifi.Index,
		},

		allNodes: allNodes,
	}

	return c, llAddr.IP, nil
}

// Close closes the Conn's underlying connection.
func (c *Conn) Close() error {
	return c.pc.Close()
}

// WriteTo writes a Message to the Conn, with an optional control message and
// destination network address.
//
// If cm is nil, a default control message will be sent.  If dst is nil, the
// message will be sent to the IPv6 link-local all nodes address for the Conn.
func (c *Conn) WriteTo(m Message, cm *ipv6.ControlMessage, dst net.IP) error {
	b, err := MarshalMessage(m)
	if err != nil {
		return err
	}

	// Set reasonable defaults if control message or destination are nil.
	if cm == nil {
		cm = c.cm
	}

	addr := &net.IPAddr{IP: dst}
	if dst == nil {
		addr = c.allNodes
	}

	_, err = c.pc.WriteTo(b, cm, addr)
	return err
}

// linkLocalPrefix is the IPv6 link-local prefix fe80::/10.
var linkLocalPrefix = &net.IPNet{
	IP:   net.ParseIP("fe80::"),
	Mask: net.CIDRMask(10, 128),
}

// linkLocalAddr searches for a valid IPv6 link-local address for the specified
// interface.
func linkLocalAddr(ifi *net.Interface) (*net.IPAddr, error) {
	addrs, err := ifi.Addrs()
	if err != nil {
		return nil, err
	}

	for _, a := range addrs {
		ipn, ok := a.(*net.IPNet)
		if !ok {
			continue
		}

		if err := checkIPv6(ipn.IP); err != nil {
			continue
		}

		if !linkLocalPrefix.Contains(ipn.IP) {
			continue
		}

		return &net.IPAddr{
			IP:   ipn.IP,
			Zone: ifi.Name,
		}, nil
	}

	return nil, fmt.Errorf("ndp: no link local address for interface %q", ifi.Name)
}
<commit_msg>ndp: code review feedback for Conn<commit_after>package ndp

import (
	"fmt"
	"net"

	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv6"
)

// HopLimit is the expected IPv6 hop limit for all NDP messages.
const HopLimit = 255

// A Conn is a Neighbor Discovery Protocol connection.
type Conn struct {
	pc *ipv6.PacketConn
	cm *ipv6.ControlMessage

	allNodes *net.IPAddr
}

// Dial dials a NDP connection using the specified interface.  It returns
// a Conn and the link-local IPv6 address of the interface.
func Dial(ifi *net.Interface) (*Conn, net.IP, error) {
	llAddr, err := linkLocalAddr(ifi)
	if err != nil {
		return nil, nil, err
	}

	ic, err := icmp.ListenPacket("ip6:ipv6-icmp", llAddr.String())
	if err != nil {
		return nil, nil, err
	}

	// Join the "all nodes" multicast group for this interface.
	allNodes := &net.IPAddr{
		IP:   net.IPv6linklocalallnodes,
		Zone: ifi.Name,
	}

	pc := ic.IPv6PacketConn()
	if err := pc.JoinGroup(ifi, allNodes); err != nil {
		return nil, nil, err
	}

	// Calculate and place ICMPv6 checksum at correct offset in all messages.
	const chkOff = 2
	if err := pc.SetChecksum(true, chkOff); err != nil {
		return nil, nil, err
	}

	c := &Conn{
		pc: pc,

		// The default control message used when none is specified.
		cm: &ipv6.ControlMessage{
			HopLimit: HopLimit,
			Src:      llAddr.IP,
			IfIndex:  ifi.Index,
		},

		allNodes: allNodes,
	}

	return c, llAddr.IP, nil
}

// Close closes the Conn's underlying connection.
func (c *Conn) Close() error {
	return c.pc.Close()
}

// WriteTo writes a Message to the Conn, with an optional control message and
// destination network address.
//
// If cm is nil, a default control message will be sent.
func (c *Conn) WriteTo(m Message, cm *ipv6.ControlMessage, dst net.IP) error {
	b, err := MarshalMessage(m)
	if err != nil {
		return err
	}

	// Set reasonable defaults if control message or destination are nil.
	if cm == nil {
		cm = c.cm
	}

	addr := &net.IPAddr{IP: dst}
	_, err = c.pc.WriteTo(b, cm, addr)
	return err
}

// linkLocalAddr searches for a valid IPv6 link-local address for the specified
// interface.
func linkLocalAddr(ifi *net.Interface) (*net.IPAddr, error) {
	addrs, err := ifi.Addrs()
	if err != nil {
		return nil, err
	}

	for _, a := range addrs {
		ipn, ok := a.(*net.IPNet)
		if !ok {
			continue
		}

		if err := checkIPv6(ipn.IP); err != nil {
			continue
		}

		if !ipn.IP.IsLinkLocalUnicast() {
			continue
		}

		return &net.IPAddr{
			IP:   ipn.IP,
			Zone: ifi.Name,
		}, nil
	}

	return nil, fmt.Errorf("ndp: no link local address for interface %q", ifi.Name)
}
<|endoftext|>


<commit_before>/*
Package routeros is a pure Go client library for accessing Mikrotik devices using the RouterOS API.
*/
package routeros

import (
	"crypto/md5"
	"crypto/tls"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"net"
	"sync"

	"github.com/go-routeros/routeros/proto"
)

// Client is a RouterOS API client.
type Client struct {
	Address  string
	Username string
	Password string
	Queue    int
	conn     net.Conn
	r        proto.Reader
	w        *proto.Writer
	closing  bool
	async    bool
	nextTag  int64
	tags     map[string]sentenceProcessor
	sync.Mutex
}

// Connect connects and login to the RouterOS device.
func (c *Client) Connect() error {
	if c.conn != nil {
		return errAlreadyConnected
	}
	conn, err := net.Dial("tcp", c.Address)
	if err != nil {
		return err
	}
	return c.connect(conn)
}

// ConnectTLS connects and login to the RouterOS device using TLS.
func (c *Client) ConnectTLS(tlsConfig *tls.Config) error {
	if c.conn != nil {
		return errAlreadyConnected
	}
	conn, err := tls.Dial("tcp", c.Address, tlsConfig)
	if err != nil {
		return err
	}
	return c.connect(conn)
}

// Close closes the connection to the RouterOS device.
func (c *Client) Close() {
	if c.conn == nil {
		return
	}
	c.closing = true
	c.conn.Close()
}

func (c *Client) connect(conn net.Conn) error {
	c.conn = conn
	c.r = proto.NewReader(conn)
	c.w = proto.NewWriter(conn)

	err := c.login()
	if err != nil {
		c.Close()
		return err
	}

	return nil
}

func (c *Client) login() error {
	r, err := c.Run("/login")
	if err != nil {
		return err
	}
	ret, ok := r.Done.Map["ret"]
	if !ok {
		return errors.New("RouterOS: /login: no ret (challenge) received")
	}
	b, err := hex.DecodeString(ret)
	if err != nil {
		return fmt.Errorf("RouterOS: /login: invalid ret (challenge) hex string received: %s", err)
	}

	r, err = c.Run("/login", "=name="+c.Username, "=response="+c.challengeResponse(b))
	if err != nil {
		return err
	}

	return nil
}

func (c *Client) challengeResponse(cha []byte) string {
	h := md5.New()
	h.Write([]byte{0})
	io.WriteString(h, c.Password)
	h.Write(cha)
	return fmt.Sprintf("00%x", h.Sum(nil))
}
<commit_msg>Call Close() only once.<commit_after>/*
Package routeros is a pure Go client library for accessing Mikrotik devices using the RouterOS API.
*/
package routeros

import (
	"crypto/md5"
	"crypto/tls"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"net"
	"sync"

	"github.com/go-routeros/routeros/proto"
)

// Client is a RouterOS API client.
type Client struct {
	Address  string
	Username string
	Password string
	Queue    int
	conn     net.Conn
	r        proto.Reader
	w        *proto.Writer
	closing  bool
	async    bool
	nextTag  int64
	tags     map[string]sentenceProcessor
	sync.Mutex
}

// Connect connects and login to the RouterOS device.
func (c *Client) Connect() error {
	if c.conn != nil {
		return errAlreadyConnected
	}
	conn, err := net.Dial("tcp", c.Address)
	if err != nil {
		return err
	}
	return c.connect(conn)
}

// ConnectTLS connects and login to the RouterOS device using TLS.
func (c *Client) ConnectTLS(tlsConfig *tls.Config) error {
	if c.conn != nil {
		return errAlreadyConnected
	}
	conn, err := tls.Dial("tcp", c.Address, tlsConfig)
	if err != nil {
		return err
	}
	return c.connect(conn)
}

// Close closes the connection to the RouterOS device.
func (c *Client) Close() {
	if c.conn == nil || c.closing {
		return
	}
	c.closing = true
	c.conn.Close()
}

func (c *Client) connect(conn net.Conn) error {
	c.conn = conn
	c.r = proto.NewReader(conn)
	c.w = proto.NewWriter(conn)

	err := c.login()
	if err != nil {
		c.Close()
		return err
	}

	return nil
}

func (c *Client) login() error {
	r, err := c.Run("/login")
	if err != nil {
		return err
	}
	ret, ok := r.Done.Map["ret"]
	if !ok {
		return errors.New("RouterOS: /login: no ret (challenge) received")
	}
	b, err := hex.DecodeString(ret)
	if err != nil {
		return fmt.Errorf("RouterOS: /login: invalid ret (challenge) hex string received: %s", err)
	}

	r, err = c.Run("/login", "=name="+c.Username, "=response="+c.challengeResponse(b))
	if err != nil {
		return err
	}

	return nil
}

func (c *Client) challengeResponse(cha []byte) string {
	h := md5.New()
	h.Write([]byte{0})
	io.WriteString(h, c.Password)
	h.Write(cha)
	return fmt.Sprintf("00%x", h.Sum(nil))
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"os"
	"strings"

	"github.com/nsf/termbox-go"
)

func client() {
	logFile, err := os.Create(gLogPath)
	if err != nil {
		panic(err)
	}
	defer os.Remove(gLogPath)
	defer logFile.Close()
	log.SetOutput(logFile)

	log.Print("hi!")

	if err := termbox.Init(); err != nil {
		log.Fatalf("initializing termbox: %s", err)
	}
	defer termbox.Close()

	app := newApp()

	app.ui.loadFile(app.nav)

	var serverChan <-chan expr

	c, err := net.Dial(gSocketProt, gSocketPath)
	if err != nil {
		msg := fmt.Sprintf("connecting server: %s", err)
		app.ui.message = msg
		log.Printf(msg)
	} else {
		serverChan = readExpr(c)
	}

	if err := app.nav.sync(); err != nil {
		msg := fmt.Sprintf("sync: %s", err)
		app.ui.message = msg
		log.Printf(msg)
	}

	if _, err := os.Stat(gConfigPath); err == nil {
		log.Printf("reading configuration file: %s", gConfigPath)

		rcFile, err := os.Open(gConfigPath)
		if err != nil {
			msg := fmt.Sprintf("opening configuration file: %s", err)
			app.ui.message = msg
			log.Printf(msg)
		}
		defer rcFile.Close()

		p := newParser(rcFile)
		for p.parse() {
			p.expr.eval(app, nil)
		}

		if p.err != nil {
			app.ui.message = p.err.Error()
			log.Print(p.err)
		}
	}

	app.ui.draw(app.nav)

	app.handleInp(serverChan)
}

func readExpr(c net.Conn) <-chan expr {
	ch := make(chan expr)

	go func() {
		fmt.Fprintf(c, "conn %d\n", gClientID)

		s := bufio.NewScanner(c)
		for s.Scan() {
			log.Printf("recv: %s", s.Text())
			p := newParser(strings.NewReader(s.Text()))
			if p.parse() {
				ch <- p.expr
			}
		}

		c.Close()
	}()

	return ch
}

func saveFiles(list []string, copy bool) error {
	c, err := net.Dial(gSocketProt, gSocketPath)
	if err != nil {
		return fmt.Errorf("dialing to save files: %s", err)
	}
	defer c.Close()

	log.Printf("saving files: %v", list)

	fmt.Fprintln(c, "save")

	if copy {
		fmt.Fprintln(c, "copy")
	} else {
		fmt.Fprintln(c, "move")
	}

	for _, f := range list {
		fmt.Fprintln(c, f)
	}
	fmt.Fprintln(c)

	return nil
}

func loadFiles() (list []string, copy bool, err error) {
	c, e := net.Dial(gSocketProt, gSocketPath)
	if e != nil {
		err = fmt.Errorf("dialing to load files: %s", e)
		return
	}
	defer c.Close()

	fmt.Fprintln(c, "load")

	s := bufio.NewScanner(c)

	s.Scan()

	switch s.Text() {
	case "copy":
		copy = true
	case "move":
		copy = false
	default:
		err = fmt.Errorf("unexpected option to copy file(s): %s", s.Text())
		return
	}

	for s.Scan() && s.Text() != "" {
		list = append(list, s.Text())
	}

	if s.Err() != nil {
		err = fmt.Errorf("scanning file list: %s", s.Err())
		return
	}

	log.Printf("loading files: %v", list)

	return
}

func sendRemote(cmd string) error {
	c, err := net.Dial(gSocketProt, gSocketPath)
	if err != nil {
		return fmt.Errorf("dialing to send server: %s", err)
	}
	defer c.Close()

	fmt.Fprintln(c, cmd)

	return nil
}
<commit_msg>make remote flag handle save/load commands<commit_after>package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"strings"

	"github.com/nsf/termbox-go"
)

func client() {
	logFile, err := os.Create(gLogPath)
	if err != nil {
		panic(err)
	}
	defer os.Remove(gLogPath)
	defer logFile.Close()
	log.SetOutput(logFile)

	log.Print("hi!")

	if err := termbox.Init(); err != nil {
		log.Fatalf("initializing termbox: %s", err)
	}
	defer termbox.Close()

	app := newApp()

	app.ui.loadFile(app.nav)

	var serverChan <-chan expr

	c, err := net.Dial(gSocketProt, gSocketPath)
	if err != nil {
		msg := fmt.Sprintf("connecting server: %s", err)
		app.ui.message = msg
		log.Printf(msg)
	} else {
		serverChan = readExpr(c)
	}

	if err := app.nav.sync(); err != nil {
		msg := fmt.Sprintf("sync: %s", err)
		app.ui.message = msg
		log.Printf(msg)
	}

	if _, err := os.Stat(gConfigPath); err == nil {
		log.Printf("reading configuration file: %s", gConfigPath)

		rcFile, err := os.Open(gConfigPath)
		if err != nil {
			msg := fmt.Sprintf("opening configuration file: %s", err)
			app.ui.message = msg
			log.Printf(msg)
		}
		defer rcFile.Close()

		p := newParser(rcFile)
		for p.parse() {
			p.expr.eval(app, nil)
		}

		if p.err != nil {
			app.ui.message = p.err.Error()
			log.Print(p.err)
		}
	}

	app.ui.draw(app.nav)

	app.handleInp(serverChan)
}

func readExpr(c net.Conn) <-chan expr {
	ch := make(chan expr)

	go func() {
		fmt.Fprintf(c, "conn %d\n", gClientID)

		s := bufio.NewScanner(c)
		for s.Scan() {
			log.Printf("recv: %s", s.Text())
			p := newParser(strings.NewReader(s.Text()))
			if p.parse() {
				ch <- p.expr
			}
		}

		c.Close()
	}()

	return ch
}

func saveFiles(list []string, copy bool) error {
	c, err := net.Dial(gSocketProt, gSocketPath)
	if err != nil {
		return fmt.Errorf("dialing to save files: %s", err)
	}
	defer c.Close()

	log.Printf("saving files: %v", list)

	fmt.Fprintln(c, "save")

	if copy {
		fmt.Fprintln(c, "copy")
	} else {
		fmt.Fprintln(c, "move")
	}

	for _, f := range list {
		fmt.Fprintln(c, f)
	}
	fmt.Fprintln(c)

	return nil
}

func loadFiles() (list []string, copy bool, err error) {
	c, e := net.Dial(gSocketProt, gSocketPath)
	if e != nil {
		err = fmt.Errorf("dialing to load files: %s", e)
		return
	}
	defer c.Close()

	fmt.Fprintln(c, "load")

	s := bufio.NewScanner(c)

	s.Scan()

	switch s.Text() {
	case "copy":
		copy = true
	case "move":
		copy = false
	default:
		err = fmt.Errorf("unexpected option to copy file(s): %s", s.Text())
		return
	}

	for s.Scan() && s.Text() != "" {
		list = append(list, s.Text())
	}

	if s.Err() != nil {
		err = fmt.Errorf("scanning file list: %s", s.Err())
		return
	}

	log.Printf("loading files: %v", list)

	return
}

func sendRemote(cmd string) error {
	c, err := net.Dial(gSocketProt, gSocketPath)
	if err != nil {
		return fmt.Errorf("dialing to send server: %s", err)
	}

	fmt.Fprintln(c, cmd)

	// XXX: Standard net.Conn interface does not include a CloseWrite method
	// but net.UnixConn and net.TCPConn implement it so the following should be
	// safe as long as we do not use other types of connections. We need
	// CloseWrite to notify the server that this is not a persistent connection
	// and it should be closed after the response.
	if v, ok := c.(interface {
		CloseWrite() error
	}); ok {
		v.CloseWrite()
	}

	io.Copy(os.Stdout, c)

	c.Close()

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
)

const (
	ChrBank2k0000 = 0
	ChrBank2k0800 = 1
	ChrBank1k1000 = 2
	ChrBank1k1400 = 3
	ChrBank1k1800 = 4
	ChrBank1k1C00 = 5
	PrgBank8k8000 = 6
	PrgBank8kA000 = 7

	PrgBankSwapModeLow  = 0
	PrgBankSwapModeHigh = 1

	ChrA12InversionModeLow  = 0
	ChrA12InversionModeHigh = 1

	RegisterBankSelect = iota
	RegisterBankData
	RegisterMirroring
	RegisterPrgRamProtect
	RegisterIrqLatch
	RegisterIrqReload
	RegisterIrqDisable
	RegisterIrqEnable
)

type Mmc3 struct {
	RomBanks  [][]Word
	VromBanks [][]Word

	PrgBankCount int
	ChrRomCount  int
	Battery      bool
	Data         []byte

	BankSelection         int
	PrgBankMode           int
	ChrA12Inversion       int
	AddressChanged        bool
	IrqEnabled            bool
	IrqCounterReloadValue Word
	IrqCounter            Word
	IrqReset              bool

	RamProtectDest [16]int
}

func NewMmc3(r *Rom) *Mmc3 {
	m := &Mmc3{
		RomBanks:     r.RomBanks,
		VromBanks:    r.VromBanks,
		PrgBankCount: r.PrgBankCount,
		ChrRomCount:  r.ChrRomCount,
		Battery:      r.Battery,
		Data:         r.Data,
	}

	// This just needs to be non-zero and not a 1
	// so that it can be caught when it's changed
	m.PrgBankMode = 10

	m.LoadRom()

	return m
}

func (m *Mmc3) LoadRom() {
	// The PRG banks are 8192 bytes in size, half the size of an
	// iNES PRG bank. If your emulator or copier handles PRG data
	// in 16384 byte chunks, you can think of the lower bit as
	// selecting the first or second half of the bank
	//
	// http://forums.nesdev.com/viewtopic.php?p=38182#p38182

	// Write hardwired PRG banks (0xC000 and 0xE000)
	m.Write8kRamBank((len(m.RomBanks)-1)*2, 0xC000)
	m.Write8kRamBank(((len(m.RomBanks)-1)*2)+1, 0xE000)

	// Write swappable PRG banks (0x8000 and 0xA000)
	m.Write8kRamBank(0, 0x8000)
	m.Write8kRamBank(1, 0xA000)
}

func (m *Mmc3) BatteryBacked() bool {
	return m.Battery
}

func (m *Mmc3) Write(v Word, a int) {
	switch m.RegisterNumber(a) {
	case RegisterBankSelect:
		m.BankSelect(int(v))
	case RegisterBankData:
		m.BankData(int(v))
	case RegisterMirroring:
		m.SetMirroring(int(v))
	case RegisterPrgRamProtect:
		m.RamProtection(int(v))
	case RegisterIrqLatch:
		m.IrqLatch(int(v))
	case RegisterIrqReload:
		m.IrqReload(int(v))
	case RegisterIrqDisable:
		m.IrqDisable(int(v))
	case RegisterIrqEnable:
		m.IrqEnable(int(v))
	}
}

func (m *Mmc3) RegisterNumber(a int) int {
	switch {
	case a >= 0x8000 && a <= 0x9FFF:
		if a%2 == 0 {
			return RegisterBankSelect
		} else {
			return RegisterBankData
		}
	case a >= 0xA000 && a <= 0xBFFF:
		if a%2 == 0 {
			return RegisterMirroring
		} else {
			return RegisterPrgRamProtect
		}
	case a >= 0xC000 && a <= 0xDFFF:
		if a%2 == 0 {
			return RegisterIrqLatch
		} else {
			return RegisterIrqReload
		}
	}

	if a%2 == 0 {
		return RegisterIrqDisable
	}

	return RegisterIrqEnable
}

func (m *Mmc3) BankSelect(v int) {
	// Next bank register to update
	m.BankSelection = v & 0x7

	address := (v >> 6) & 0x1
	if m.PrgBankMode != address {
		m.AddressChanged = true
	}

	m.PrgBankMode = address
	m.ChrA12Inversion = (v >> 7) & 0x1
}

func (m *Mmc3) BankData(v int) {
	loadHardBanks := func() {
		if m.AddressChanged {
			if m.PrgBankMode == PrgBankSwapModeLow {
				//fmt.Println("Changed address high")
				m.Write8kRamBank((len(m.RomBanks)-1)*2, 0xC000)
			} else {
				//fmt.Println("Changed address low")
				m.Write8kRamBank((len(m.RomBanks)-1)*2, 0x8000)
			}

			m.AddressChanged = false
		}
	}

	switch m.BankSelection {
	case ChrBank2k0000:
		if m.ChrRomCount == 0 {
			break
		}

		//fmt.Printf("2k @ 0x0000: ")
		if m.ChrA12Inversion == ChrA12InversionModeLow {
			//fmt.Printf("ModeLow CHR on bank -> %d\n", v)
			m.Write1kVramBank(v, 0x0000)
			m.Write1kVramBank(v+1, 0x0400)
		} else {
			//fmt.Printf("ModeHigh CHR on bank -> %d\n", v)
			m.Write1kVramBank(v, 0x1000)
			m.Write1kVramBank(v+1, 0x1400)
		}
	case ChrBank2k0800:
		if m.ChrRomCount == 0 {
			break
		}

		//fmt.Printf("2k @ 0x0800: ")
		if m.ChrA12Inversion == ChrA12InversionModeLow {
			//fmt.Printf("ModeLow CHR on bank -> %d\n", v)
			m.Write1kVramBank(v, 0x0800)
			m.Write1kVramBank(v+1, 0x0C00)
		} else {
			//fmt.Printf("ModeHigh CHR on bank -> %d\n", v)
			m.Write1kVramBank(v, 0x1800)
			m.Write1kVramBank(v+1, 0x1C00)
		}
	case ChrBank1k1000:
		if m.ChrRomCount == 0 {
			break
		}

		//fmt.Printf("1k @ 0x1000: ")
		if m.ChrA12Inversion == ChrA12InversionModeLow {
			//fmt.Printf("ModeLow CHR on bank -> %d\n", v)
			m.Write1kVramBank(v, 0x1000)
		} else {
			//fmt.Printf("ModeHigh CHR on bank -> %d\n", v)
			m.Write1kVramBank(v, 0x0000)
		}
	case ChrBank1k1400:
		if m.ChrRomCount == 0 {
			break
		}

		//fmt.Printf("1k @ 0x1400: ")
		if m.ChrA12Inversion == ChrA12InversionModeLow {
			//fmt.Printf("ModeLow CHR on bank -> %d\n", v)
			m.Write1kVramBank(v, 0x1400)
		} else {
			//fmt.Printf("ModeHigh CHR on bank -> %d\n", v)
			m.Write1kVramBank(v, 0x0400)
		}
	case ChrBank1k1800:
		if m.ChrRomCount == 0 {
			break
		}

		//fmt.Printf("1k @ 0x1800: ")
		if m.ChrA12Inversion == ChrA12InversionModeLow {
			//fmt.Printf("ModeLow CHR on bank -> %d\n", v)
			m.Write1kVramBank(v, 0x1800)
		} else {
			//fmt.Printf("ModeHigh CHR on bank -> %d\n", v)
			m.Write1kVramBank(v, 0x0800)
		}
	case ChrBank1k1C00:
		if m.ChrRomCount == 0 {
			break
		}

		//fmt.Printf("1k @ 0x1C00: ")
		if m.ChrA12Inversion == ChrA12InversionModeLow {
			//fmt.Printf("ModeLow CHR on bank -> %d\n", v)
			m.Write1kVramBank(v, 0x1C00)
		} else {
			//fmt.Printf("ModeHigh CHR on bank -> %d\n", v)
			m.Write1kVramBank(v, 0x0C00)
		}
	case PrgBank8k8000:
		loadHardBanks()

		if m.PrgBankMode == PrgBankSwapModeLow {
			//fmt.Printf("0x%X: Low mode (0x8000) PRG switch on bank -> %d\n", ProgramCounter, v)
			m.Write8kRamBank(v, 0x8000)
		} else {
			//fmt.Printf("0x%X: High mode (0xC000) PRG switch on bank -> %d\n", ProgramCounter, v)
			m.Write8kRamBank(v, 0xC000)
		}
	case PrgBank8kA000:
		//fmt.Printf("0x%X: 8k 0xA000 PRG switch on bank -> %d\n", ProgramCounter, v)
		m.Write8kRamBank(v, 0xA000)

		loadHardBanks()
	}
}

func (m *Mmc3) SetMirroring(v int) {
	switch v & 0x1 {
	case 0x0:
		ppu.Nametables.SetMirroring(MirroringVertical)
	case 0x1:
		ppu.Nametables.SetMirroring(MirroringHorizontal)
	}
}

func (m *Mmc3) RamProtection(v int) {
	// TODO: WhAT IS THIS I DON'T EVEN
	fmt.Println("RamProtection register")
}

func (m *Mmc3) IrqLatch(v int) {
	// $C000
	m.IrqCounterReloadValue = Word(v)
}

func (m *Mmc3) IrqReload(v int) {
	// $C001
	m.IrqReset = true
}

func (m *Mmc3) IrqDisable(v int) {
	// $E001
	m.IrqEnabled = false
	m.IrqCounter = m.IrqCounterReloadValue
}

func (m *Mmc3) IrqEnable(v int) {
	// $E001
	m.IrqEnabled = true
}

func (m *Mmc3) Write8kRamBank(bank, dest int) {
	if m.RamProtectDest[dest>>12] == bank {
		return
	}
	m.RamProtectDest[dest>>12] = bank

	b := (bank >> 1) % len(m.RomBanks)
	offset := (bank % 2) * 0x2000

	WriteOffsetRamBank(m.RomBanks, b, dest, Size8k, offset)
}

func (m *Mmc3) Write1kVramBank(bank, dest int) {
	b := (bank >> 2) % len(m.VromBanks)
	offset := (bank % 4) * 0x400

	//fmt.Printf("Updating bank: %d\n", b)
	//fmt.Printf("Upper 1k offset: %d\n", offset)

	WriteOffsetVramBank(m.VromBanks, b, dest, Size1k, offset)
}

func (m *Mmc3) Hook() {
	if (ppu.Scanline > 20 && ppu.Scanline < 240) && (ppu.ShowBackground || ppu.ShowSprites) {
		// A12 Rising Edge
		if m.IrqReset {
			m.IrqCounter = m.IrqCounterReloadValue
			m.IrqReset = false
		} else if m.IrqCounter > 0 {
			if ppu.SpritePatternAddress != ppu.BackgroundPatternAddress && (ppu.SpritePatternAddress > 0 || ppu.BackgroundPatternAddress > 0) {
				m.IrqCounter--
			}
		}

		if m.IrqCounter == 0 {
			if m.IrqEnabled {
				cpu.RequestInterrupt(InterruptIrq)
			}
		}
	}
}
<commit_msg>Continued MMC3 work<commit_after>package main

import (
	"fmt"
)

const (
	ChrBank2k0000 = 0
	ChrBank2k0800 = 1
	ChrBank1k1000 = 2
	ChrBank1k1400 = 3
	ChrBank1k1800 = 4
	ChrBank1k1C00 = 5
	PrgBank8k8000 = 6
	PrgBank8kA000 = 7

	PrgBankSwapModeLow  = 0
	PrgBankSwapModeHigh = 1

	ChrA12InversionModeLow  = 0
	ChrA12InversionModeHigh = 1

	RegisterBankSelect = iota
	RegisterBankData
	RegisterMirroring
	RegisterPrgRamProtect
	RegisterIrqLatch
	RegisterIrqReload
	RegisterIrqDisable
	RegisterIrqEnable
)

type Mmc3 struct {
	RomBanks  [][]Word
	VromBanks [][]Word

	PrgBankCount int
	ChrRomCount  int
	Battery      bool
	Data         []byte

	BankSelection   int
	PrgBankMode     int
	ChrA12Inversion int
	AddressChanged  bool
	IrqEnabled      bool
	IrqLatchValue   Word
	IrqCounter      Word
	IrqPresent      bool
	IrqPresentVbl   bool

	RamProtectDest [16]int
}

func NewMmc3(r *Rom) *Mmc3 {
	m := &Mmc3{
		RomBanks:     r.RomBanks,
		VromBanks:    r.VromBanks,
		PrgBankCount: r.PrgBankCount,
		ChrRomCount:  r.ChrRomCount,
		Battery:      r.Battery,
		Data:         r.Data,
	}

	// This just needs to be non-zero and not a 1
	// so that it can be caught when it's changed
	m.PrgBankMode = 10

	m.LoadRom()

	return m
}

func (m *Mmc3) LoadRom() {
	// The PRG banks are 8192 bytes in size, half the size of an
	// iNES PRG bank. If your emulator or copier handles PRG data
	// in 16384 byte chunks, you can think of the lower bit as
	// selecting the first or second half of the bank
	//
	// http://forums.nesdev.com/viewtopic.php?p=38182#p38182

	// Write hardwired PRG banks (0xC000 and 0xE000)
	m.Write8kRamBank((len(m.RomBanks)-1)*2, 0xC000)
	m.Write8kRamBank(((len(m.RomBanks)-1)*2)+1, 0xE000)

	// Write swappable PRG banks (0x8000 and 0xA000)
	m.Write8kRamBank(0, 0x8000)
	m.Write8kRamBank(1, 0xA000)
}

func (m *Mmc3) BatteryBacked() bool {
	return m.Battery
}

func (m *Mmc3) Write(v Word, a int) {
	switch m.RegisterNumber(a) {
	case RegisterBankSelect:
		m.BankSelect(int(v))
	case RegisterBankData:
		m.BankData(int(v))
	case RegisterMirroring:
		m.SetMirroring(int(v))
	case RegisterPrgRamProtect:
		m.RamProtection(int(v))
	case RegisterIrqLatch:
		m.IrqLatch(int(v))
	case RegisterIrqReload:
		m.IrqReload(int(v))
	case RegisterIrqDisable:
		m.IrqDisable(int(v))
	case RegisterIrqEnable:
		m.IrqEnable(int(v))
	}
}

func (m *Mmc3) RegisterNumber(a int) int {
	switch {
	case a >= 0x8000 && a <= 0x9FFF:
		if a%2 == 0 {
			return RegisterBankSelect
		} else {
			return RegisterBankData
		}
	case a >= 0xA000 && a <= 0xBFFF:
		if a%2 == 0 {
			return RegisterMirroring
		} else {
			return RegisterPrgRamProtect
		}
	case a >= 0xC000 && a <= 0xDFFF:
		if a%2 == 0 {
			return RegisterIrqLatch
		} else {
			return RegisterIrqReload
		}
	}

	if a%2 == 0 {
		return RegisterIrqDisable
	}

	return RegisterIrqEnable
}

func (m *Mmc3) BankSelect(v int) {
	// Next bank register to update
	m.BankSelection = v & 0x7

	address := (v >> 6) & 0x1
	if m.PrgBankMode != address {
		m.AddressChanged = true
	}

	m.PrgBankMode = address
	m.ChrA12Inversion = (v >> 7) & 0x1
}

func (m *Mmc3) BankData(v int) {
	loadHardBanks := func() {
		if m.AddressChanged {
			if m.PrgBankMode == PrgBankSwapModeLow {
				m.Write8kRamBank((len(m.RomBanks)-1)*2, 0xC000)
			} else {
				m.Write8kRamBank((len(m.RomBanks)-1)*2, 0x8000)
			}

			m.AddressChanged = false
		}
	}

	switch m.BankSelection {
	case ChrBank2k0000:
		if m.ChrRomCount == 0 {
			break
		}

		if m.ChrA12Inversion == ChrA12InversionModeLow {
			m.Write1kVramBank(v, 0x0000)
			m.Write1kVramBank(v+1, 0x0400)
		} else {
			m.Write1kVramBank(v, 0x1000)
			m.Write1kVramBank(v+1, 0x1400)
		}
	case ChrBank2k0800:
		if m.ChrRomCount == 0 {
			break
		}

		if m.ChrA12Inversion == ChrA12InversionModeLow {
			m.Write1kVramBank(v, 0x0800)
			m.Write1kVramBank(v+1, 0x0C00)
		} else {
			m.Write1kVramBank(v, 0x1800)
			m.Write1kVramBank(v+1, 0x1C00)
		}
	case ChrBank1k1000:
		if m.ChrRomCount == 0 {
			break
		}

		if m.ChrA12Inversion == ChrA12InversionModeLow {
			m.Write1kVramBank(v, 0x1000)
		} else {
			m.Write1kVramBank(v, 0x0000)
		}
	case ChrBank1k1400:
		if m.ChrRomCount == 0 {
			break
		}

		if m.ChrA12Inversion == ChrA12InversionModeLow {
			m.Write1kVramBank(v, 0x1400)
		} else {
			m.Write1kVramBank(v, 0x0400)
		}
	case ChrBank1k1800:
		if m.ChrRomCount == 0 {
			break
		}

		if m.ChrA12Inversion == ChrA12InversionModeLow {
			m.Write1kVramBank(v, 0x1800)
		} else {
			m.Write1kVramBank(v, 0x0800)
		}
	case ChrBank1k1C00:
		if m.ChrRomCount == 0 {
			break
		}

		if m.ChrA12Inversion == ChrA12InversionModeLow {
			m.Write1kVramBank(v, 0x1C00)
		} else {
			m.Write1kVramBank(v, 0x0C00)
		}
	case PrgBank8k8000:
		loadHardBanks()

		if m.PrgBankMode == PrgBankSwapModeLow {
			m.Write8kRamBank(v, 0x8000)
		} else {
			m.Write8kRamBank(v, 0xC000)
		}
	case PrgBank8kA000:
		m.Write8kRamBank(v, 0xA000)

		loadHardBanks()
	}
}

func (m *Mmc3) SetMirroring(v int) {
	switch v & 0x1 {
	case 0x0:
		ppu.Nametables.SetMirroring(MirroringVertical)
	case 0x1:
		ppu.Nametables.SetMirroring(MirroringHorizontal)
	}
}

func (m *Mmc3) RamProtection(v int) {
	// TODO: WhAT IS THIS I DON'T EVEN
	fmt.Println("RamProtection register")
}

func (m *Mmc3) IrqLatch(v int) {
	// $C000
	m.IrqLatchValue = Word(v)
}

func (m *Mmc3) IrqReload(v int) {
	// $C001
	if ppu.Scanline < 241 {
		m.IrqCounter |= 0x80
		m.IrqPresent = true
	} else {
		m.IrqCounter |= 0x80
		m.IrqPresentVbl = true
		m.IrqPresent = false
	}
}

func (m *Mmc3) IrqDisable(v int) {
	// $E001
	m.IrqEnabled = false
	m.IrqCounter = m.IrqLatchValue
}

func (m *Mmc3) IrqEnable(v int) {
	// $E001
	m.IrqEnabled = true
}

func (m *Mmc3) Write8kRamBank(bank, dest int) {
	if m.RamProtectDest[dest>>12] == bank {
		return
	}
	m.RamProtectDest[dest>>12] = bank

	b := (bank >> 1) % len(m.RomBanks)
	offset := (bank % 2) * 0x2000

	WriteOffsetRamBank(m.RomBanks, b, dest, Size8k, offset)
}

func (m *Mmc3) Write1kVramBank(bank, dest int) {
	b := (bank >> 2) % len(m.VromBanks)
	offset := (bank % 4) * 0x400

	WriteOffsetVramBank(m.VromBanks, b, dest, Size1k, offset)
}

func (m *Mmc3) Hook() {
	// A12 Rising Edge
	if m.IrqPresentVbl {
		m.IrqCounter = m.IrqLatchValue
		m.IrqPresentVbl = false
	}

	if m.IrqPresent {
		m.IrqCounter = m.IrqLatchValue
		m.IrqPresent = false
	} else if m.IrqCounter > 0 {
		m.IrqCounter--
	}

	if m.IrqCounter == 0 {
		if m.IrqEnabled {
			cpu.RequestInterrupt(InterruptIrq)
		}
		m.IrqPresent = false
	}
}
<|endoftext|>