<commit_before>package main

import(
	"math"
	"fmt"
	"errors"
	"math/rand"
	"time"
	"sync"
	"code.google.com/p/plotinum/plot"
	"code.google.com/p/plotinum/plotter"
	"code.google.com/p/plotinum/plotutil"
)

type Result struct{
	Person Person
	Workload Workload
}

func (r Result) String()string{
	return fmt.Sprintf("<Result %v %s>", r.Person, r.Workload)
}

type Person struct{
	Vg float64 // Value/grade
	Gt float64 // Grade/time
	La float64 // Look ahead
	Vt float64 // Value/time
	P float64 // Rememberance
	B float64 // Beta
	D float64 // Delta

	Semester Semester // The schedule for the semester
	Assignments []Assignment // The assignment due
	WorkHours map[Assignment]int // The number of hours worked on assignments.
}

func (p Person) String() string{
	return fmt.Sprintf("<Person Vg:%f Gt:%f La:%f Vt:%f P:%f B:%f D:%f",
		p.Vg, p.Gt, p.La, p.Vt, p.P, p.B, p.D)
}

type Assignment struct{
	TotalGrade int // The total grade units this assignment is graded out of.
	DateDue int // The date due in numbers of day into the semester
	Name string
}

func (a Assignment) String() string{
	return fmt.Sprintf("",a.TotalGrade, a.DateDue, a.Name)
}

type Semester struct{
	Days int // The number of days
	Day int // The current day
	Weights map[int]float64 // A mapping of days to the attitude weight.
	Allowed map[int]int // A maping of days to hours available to work.
}

type Work struct{
	Day int
	Hours int
	Assignment Assignment
}

func (w Work) String() string{
	return fmt.Sprintf("<W Day=%d Hours=%d Assignment=%v",w.Day, w.Hours, w.Assignment)
}

func IntFactorial(i int)(int, error){
	if i > 20|| i < 1{return 0, errors.New("Factorial out of range")}
	if i == 1{
		return 1, nil
	}
	res, _ := IntFactorial(i-1)
	return i * res, nil
}

// Represents a possible ammount to work in the future.
type Workload struct{
	Days []Work
	Hours map[Assignment]int
}

func (w Workload) String() string {
	return fmt.Sprintf("<WL days=%v hours=%v>" ,w.Days, w.Hours)
}

// Generate workloads starting on the current day.
func (p *Person) Workloads() []Workload {

	workloads := make([]Workload,1)
	// Create base workload
	workloads[0] = *new(Workload)
	workloads[0].Days = make([]Work, 0)
	workloads[0].Hours = make(map[Assignment]int)
	for _,assignment := range(p.Assignments){
		workloads[0].Hours[assignment] = 0
	}
	// For each day until the look ahead day.
	for i := 0; float64(i)
= (p.Semester.Day + i){
					// For every time that does not put you over the limit.
					for t:=1; basewl.Hours[assignment]+
						t+p.WorkHours[assignment] <=
						assignment.TotalGrade &&
						t <= p.Semester.Allowed[p.Semester.Day + i]; t++{

						wl := *new(Workload)
						day := *new(Work)
						day.Assignment = assignment
						day.Hours = t
						day.Day = i + p.Semester.Day
						wl.Days = append(basewl.Days,day)
						wl.Hours = make(map[Assignment]int)
						for k, v := range(basewl.Hours){
							wl.Hours[k]=v
						}
						wl.Hours[assignment] = basewl.Hours[assignment] + t
						newloads = append(newloads,wl)
					}
				}
			}
			wl := *new(Workload)
			day := *new(Work)
			day.Assignment = Assignment{}
			day.Hours = 0
			day.Day = i + p.Semester.Day
			wl.Days = append(basewl.Days,day)
			wl.Hours = make(map[Assignment]int)
			for k,v := range(basewl.Hours){
				wl.Hours[k]=v
			}
			newloads = append(newloads, wl)
		}

		workloads = newloads
	}
	return workloads
}

// The discount function for this individual
// If d is 0 then the function is 1, otherwise it is B * (D ^ d)
func (p *Person) Discount(d int) float64{
	if d == 0 {
		return 1
	}
	return p.B * math.Pow(p.D, float64(d))
}

// Returns the expected utility of the given work.
func (p Person) WorkUtility(work Work) float64{
	utility := (float64(work.Hours) * p.Vg *p.Gt *p.B * p.Discount(work.Assignment.DateDue - p.Semester.Day))
	cost := (float64(work.Hours) * p.Semester.Weights[work.Day] * p.Discount(work.Day - p.Semester.Day))
	return utility -cost
}

// Returns the expected utility of the given workload evaluated on the current day.
func (p Person) Utility(workload Workload) float64{
	utility := float64(0)
	for _, work := range(workload.Days){
		if work.Assignment.TotalGrade != 0{
			utility += p.WorkUtility(work)
		}
	}
	return utility
}

func (p Person) Simulate(results chan Result){
	var globalWorkload Workload
	globalWorkload.Hours = make(map[Assignment]int)

	for p.Semester.Day
 maxUtility{
				maxUtility = utility
				choices = make([]Workload, 0)
				choices = append(choices, workload)
			}
		}

		var choice Workload
		if len(choices) > 1{
			choice = choices[rand.Intn(len(choices))]
		}else{
			choice = choices[0]
		}
		if len(choice.Days) > 0 {
			globalWorkload.Days = append(globalWorkload.Days, choice.Days[0])
			p.WorkHours[choice.Days[0].Assignment] += choice.Days[0].Hours
			globalWorkload.Hours[choice.Days[0].Assignment] += choice.Days[0].Hours
		}
	}

	results <- Result{Person:p, Workload:globalWorkload}
}

func main(){
	fmt.Println("Simulating people... This is most likely not going to work.")

	people := make([]Person, 0)
	rand.Seed(time.Now().Unix())

	assignments := make([]Assignment, 0)
	assignments = append(assignments, Assignment{DateDue:30,TotalGrade: 20, Name:"Assignment 1"})
	assignments = append(assignments, Assignment{DateDue:30,TotalGrade: 20, Name:"Assignment 2"})
	numPeople := 100
	numDays := 30
	for p:=0; p< numPeople; p++{
		// Create person
		Vg := rand.NormFloat64() * 1 + 10
		Gt := rand.NormFloat64() * 0.25 + 2
		B := rand.NormFloat64() * 0.2 + 0.5
		D := rand.NormFloat64() * 0.1 + 0.8
		if D > 1.0{
			D = 1
		}
		person := Person{Vg:Vg, Gt:Gt, La: 7, Vt: 1, P:1, B:B, D:D, WorkHours:make(map[Assignment]int)}
		semester := Semester{Days: 30,Day:0,Weights:make(map[int]float64), Allowed:make(map[int]int)}
		person.Semester = semester
		for i := 0; i <= 30; i++{
			person.Semester.Weights[i] = float64(i)*0.025
			person.Semester.Allowed[i] = 3
			if i > 25 {
				person.Semester.Allowed[i] = 2
				person.Semester.Weights[i] += float64(i)*0.025
			}
		}

		person.Assignments = assignments

		people = append(people, person)
	}

	results := make(chan Result)
	inChan := make(chan Person)

	numRoutines := 10
	var wg sync.WaitGroup
	for i := 0; i < numRoutines; i++{
		wg.Add(1)
		go func(people chan Person, results chan Result){
			for person := range(people){
				person.Simulate(results)
			}
			wg.Done()
		}(inChan, results)
	}

	//Wait for the channel simulations to finish then close the results chan
	go func(){
		wg.Wait()
		close(results)
	}()

	// Keep placing people in the channel
	go func(people []Person, inChan chan Person){
		for _,person := range people{
			inChan <- person
		}
		close(inChan)
	}(people, inChan)

	frequency := make(map[Assignment]plotter.XYs, len(assignments))
	grades := make(map[Assignment]int)
	gradeDistribution := make(plotter.XYs,101)

	for i := 0;i<= 100;i++{
		gradeDistribution[i].X = float64(i)
		gradeDistribution[i].Y = 0
	}

	for _, Assignment := range assignments{
		frequency[Assignment] = make(plotter.XYs,numDays+1)
		for i := 0;i <= numDays; i++{
			frequency[Assignment][i].X = float64(i)
			frequency[Assignment][i].Y = 0
		}
	}
	z :=0
	for res := range results{
		res := res
		fmt.Printf("Results: %v \n\t%v\n", res.Person, res.Workload)
		for n,day := range res.Workload.Days{
			if day.Assignment.TotalGrade != 0{
				frequency[day.Assignment][n].Y += float64(day.Hours)
			}
		}
		for Assignment, Grade := range res.Workload.Hours{
			if Assignment.Name != ""{
				grade := int((float64(Grade)/(float64(Assignment.TotalGrade))*float64(100)))
				gradeDistribution[grade].Y = gradeDistribution[grade].Y + float64(1)
				grades[Assignment] = grades[Assignment] + Grade
			}
		}
		z+=1
	}
	wt, err := plot.New()
	if err != nil {
			panic(err)
	}
	wt.Title.Text = "Distribution of work hours vs time."
	wt.Y.Label.Text = "Work Hours"
	wt.X.Label.Text = "Time"
	wt.Add(plotter.NewGrid())

	for assignment,hour := range grades{
		fmt.Printf("Average %s: %f\n", assignment, float64(hour)/float64(numPeople))
	}

	i := 0
	for assignment, xys := range frequency{
		lpline,lppoints, err := plotter.NewLinePoints(xys)
		if err != nil{
			panic(err)
		}
		lpline.Color = plotutil.Color(i)
		lpline.Dashes = plotutil.Dashes(i)
		lppoints.Color = plotutil.Color(i)
		lppoints.Shape = plotutil.Shape(i)
		i++
		wt.Add(lpline)
		wt.Add(lppoints)
		wt.Legend.Add(assignment.Name, lpline, lppoints)
	}
	wt.Legend.Top = true
	wt.X.Min=0
	wt.X.Max=float64(numDays)
	if err := wt.Save(5, 3, "workDistribution.png"); err != nil {
		panic(err)
	}

	gt, err := plot.New()
	if err != nil {
			panic(err)
	}
	gt.Title.Text = "Distribution of grades"
	gt.Y.Label.Text = "Frequency"
	gt.X.Label.Text = "Grades"
	gt.Add(plotter.NewGrid())

	h, err := plotter.NewHistogram(gradeDistribution, 10)
	if err != nil{
		panic(err)
	}
	h.FillColor = plotutil.Color(1)
	gt.Add(h)

	gt.Legend.Top = true
	gt.Y.Min=0
	gt.Y.Tick.Marker = func(min, max float64) []plot.Tick {
		const suggestedTicks = 3
		delta := 1
		for (max-min)/float64(delta) < float64(suggestedTicks) {
			delta += 10
		}
		ticks := make([]plot.Tick, 0)
		for i := int(min); i<= int(max); i+= delta{
			ticks = append(ticks, plot.Tick{Value:float64(i),Label:fmt.Sprintf("%d",i)})
		}
		return ticks
	}
	gt.X.Min=0
	gt.X.Max=float64(100)
	if err := gt.Save(5, 3, "gradeDistribution.png"); err != nil {
		panic(err)
	}

}
<commit_msg>Output to csv instead of graphs.<commit_after>package main

import(
	"math"
	"fmt"
	"errors"
	"math/rand"
	"time"
	"sync"
	"os"
	"flag"
)

type Result struct{
	Person Person
	Workload Workload
}

func (r Result) String()string{
	return fmt.Sprintf("<Result %v %s>", r.Person, r.Workload)
}

type Person struct{
	Vg float64 // Value/grade
	Gt float64 // Grade/time
	La float64 // Look ahead
	Vt float64 // Value/time
	P float64 // Rememberance
	B float64 // Beta
	D float64 // Delta

	Semester Semester // The schedule for the semester
	Assignments []Assignment // The assignment due
	WorkHours map[Assignment]int // The number of hours worked on assignments.
}

func (p Person) String() string{
	return fmt.Sprintf("<Person Vg:%f Gt:%f La:%f Vt:%f P:%f B:%f D:%f",
		p.Vg, p.Gt, p.La, p.Vt, p.P, p.B, p.D)
}

type Assignment struct{
	TotalGrade int // The total grade units this assignment is graded out of.
	DateDue int // The date due in numbers of day into the semester
	Name string
}

func (a Assignment) String() string{
	return fmt.Sprintf("",a.TotalGrade, a.DateDue, a.Name)
}

type Semester struct{
	Days int // The number of days
	Day int // The current day
	Weights map[int]float64 // A mapping of days to the attitude weight.
	Allowed map[int]int // A maping of days to hours available to work.
}

type Work struct{
	Day int
	Hours int
	Assignment Assignment
}

func (w Work) String() string{
	return fmt.Sprintf("<W Day=%d Hours=%d Assignment=%v",w.Day, w.Hours, w.Assignment)
}

func IntFactorial(i int)(int, error){
	if i > 20|| i < 1{return 0, errors.New("Factorial out of range")}
	if i == 1{
		return 1, nil
	}
	res, _ := IntFactorial(i-1)
	return i * res, nil
}

// Represents a possible ammount to work in the future.
type Workload struct{
	Days []Work
	Hours map[Assignment]int
}

func (w Workload) String() string {
	return fmt.Sprintf("<WL days=%v hours=%v>" ,w.Days, w.Hours)
}

// Generate workloads starting on the current day.
func (p *Person) Workloads() []Workload {

	workloads := make([]Workload,1)
	// Create base workload
	workloads[0] = *new(Workload)
	workloads[0].Days = make([]Work, 0)
	workloads[0].Hours = make(map[Assignment]int)
	for _,assignment := range(p.Assignments){
		workloads[0].Hours[assignment] = 0
	}
	// For each day until the look ahead day.
	for i := 0; float64(i)
= (p.Semester.Day + i){
					// For every time that does not put you over the limit.
					for t:=1; basewl.Hours[assignment]+
						t+p.WorkHours[assignment] <=
						assignment.TotalGrade &&
						t <= p.Semester.Allowed[p.Semester.Day + i]; t++{

						wl := *new(Workload)
						day := *new(Work)
						day.Assignment = assignment
						day.Hours = t
						day.Day = i + p.Semester.Day
						wl.Days = append(basewl.Days,day)
						wl.Hours = make(map[Assignment]int)
						for k, v := range(basewl.Hours){
							wl.Hours[k]=v
						}
						wl.Hours[assignment] = basewl.Hours[assignment] + t
						newloads = append(newloads,wl)
					}
				}
			}
			wl := *new(Workload)
			day := *new(Work)
			day.Assignment = Assignment{}
			day.Hours = 0
			day.Day = i + p.Semester.Day
			wl.Days = append(basewl.Days,day)
			wl.Hours = make(map[Assignment]int)
			for k,v := range(basewl.Hours){
				wl.Hours[k]=v
			}
			newloads = append(newloads, wl)
		}

		workloads = newloads
	}
	return workloads
}

// The discount function for this individual
// If d is 0 then the function is 1, otherwise it is B * (D ^ d)
func (p *Person) Discount(d int) float64{
	if d == 0 {
		return 1
	}
	return p.B * math.Pow(p.D, float64(d))
}

// Returns the expected utility of the given work.
func (p Person) WorkUtility(work Work) float64{
	utility := (float64(work.Hours) * p.Vg *p.Gt *p.B * p.Discount(work.Assignment.DateDue - p.Semester.Day))
	cost := (float64(work.Hours) * p.Semester.Weights[work.Day] * p.Discount(work.Day - p.Semester.Day))
	return utility -cost
}

// Returns the expected utility of the given workload evaluated on the current day.
func (p Person) Utility(workload Workload) float64{
	utility := float64(0)
	for _, work := range(workload.Days){
		if work.Assignment.TotalGrade != 0{
			utility += p.WorkUtility(work)
		}
	}
	return utility
}

func (p Person) Simulate(results chan Result){
	var globalWorkload Workload
	globalWorkload.Hours = make(map[Assignment]int)

	for p.Semester.Day
 maxUtility{
				maxUtility = utility
				choices = make([]Workload, 0)
				choices = append(choices, workload)
			}
		}

		var choice Workload
		if len(choices) > 1{
			choice = choices[rand.Intn(len(choices))]
		}else{
			choice = choices[0]
		}
		if len(choice.Days) > 0 {
			globalWorkload.Days = append(globalWorkload.Days, choice.Days[0])
			p.WorkHours[choice.Days[0].Assignment] += choice.Days[0].Hours
			globalWorkload.Hours[choice.Days[0].Assignment] += choice.Days[0].Hours
		}
	}

	results <- Result{Person:p, Workload:globalWorkload}
}

var output string
func init() {
	flag.StringVar(&output, "output", "out", "the output prefix")
}

func main(){
	flag.Parse()
	fmt.Println("Simulating people... This is most likely not going to work.")

	people := make([]Person, 0)
	rand.Seed(time.Now().Unix())

	assignments := make([]Assignment, 0)
	assignments = append(assignments, Assignment{DateDue:15,TotalGrade: 20, Name:"Assignment 1"})
	assignments = append(assignments, Assignment{DateDue:30,TotalGrade: 20, Name:"Assignment 2"})
	numPeople := 100
	numDays := 30
	for p:=0; p< numPeople; p++{
		// Create person
		Vg := rand.NormFloat64() * 1 + 10
		Gt := rand.NormFloat64() * 0.25 + 2
		B := rand.NormFloat64() * 0.5 + 0.5
		D := rand.NormFloat64() * 0.1 + 0.8
		if D > 1.0{
			D = 1
		}
		person := Person{Vg:Vg, Gt:Gt, La: 7, Vt: 1, P:1, B:B, D:D, WorkHours:make(map[Assignment]int)}
		semester := Semester{Days: 30,Day:0,Weights:make(map[int]float64), Allowed:make(map[int]int)}
		person.Semester = semester
		for i := 0; i <= 30; i++{
			person.Semester.Weights[i] = 2.0 * float64(i)*0.025
			person.Semester.Allowed[i] = 3
			if i > 25 {
				person.Semester.Allowed[i] = 2
				person.Semester.Weights[i] += float64(i)*0.050
			}
		}

		person.Assignments = assignments

		people = append(people, person)
	}

	results := make(chan Result)
	inChan := make(chan Person)

	numRoutines := 10
	var wg sync.WaitGroup
	for i := 0; i < numRoutines; i++{
		wg.Add(1)
		go func(people chan Person, results chan Result){
			for person := range(people){
				person.Simulate(results)
			}
			wg.Done()
		}(inChan, results)
	}

	//Wait for the channel simulations to finish then close the results chan
	go func(){
		wg.Wait()
		close(results)
	}()

	// Keep placing people in the channel
	go func(people []Person, inChan chan Person){
		for _,person := range people{
			inChan <- person
		}
		close(inChan)
	}(people, inChan)

	gradeDistribution := make(map[Assignment][]int, len(assignments))
	workDistribution := make(map[Assignment][]int, len(assignments))

	for _, assignment := range assignments{
		workDistribution[assignment] = make([]int, numDays)
		for i:=0; i <numDays; i++{
			workDistribution[assignment][i] = 0
		}
		gradeDistribution[assignment] = make([]int, 51)
		for i:=0; i <51; i++{
			gradeDistribution[assignment][i] = 0
		}
	}

	for res := range results{
		res := res
		for n,day := range res.Workload.Days{
			if day.Assignment.TotalGrade != 0{
				workDistribution[day.Assignment][n] += day.Hours
			}
		}
		for Assignment, Grade := range res.Workload.Hours{
			if Assignment.Name != ""{
				grade := int(float64(Grade)/(float64(Assignment.TotalGrade))*float64(50))

				gradeDistribution[Assignment][grade] += 1
			}
		}
	}

	// Output to CSV file
	wd, err := os.Create(fmt.Sprintf("%s-work.csv",output))
	if err != nil{
		panic(err)
	}
	header := "Day"
	for _, assignment := range(assignments){
		header = fmt.Sprintf("%s,%s",header, assignment.Name)
	}
	header = fmt.Sprintf("%s\n", header)
	wd.WriteString(header)

	for i:=0;i<numDays;i++{
		line := fmt.Sprintf("%d",i)
		split := ","
		for _, assignment := range(assignments){
			line = fmt.Sprintf("%s%s%d",line,split, workDistribution[assignment][i])
		}
		wd.WriteString(fmt.Sprintf("%s\n", line))
	}

	// Output grade distribution to CSV file
	gd, err := os.Create(fmt.Sprintf("%s-grade.csv",output))
	if err != nil{
		panic(err)
	}
	header = "Grade"
	for _, assignment := range(assignments){
		header = fmt.Sprintf("%s,%s",header, assignment.Name)
	}
	header = fmt.Sprintf("%s\n", header)
	gd.WriteString(header)

	for i:=0;i<51;i++{
		line := fmt.Sprintf("%d",i*2)
		split := ","
		for _, assignment := range(assignments){
			line = fmt.Sprintf("%s%s%d",line,split, gradeDistribution[assignment][i])
		}
		gd.WriteString(fmt.Sprintf("%s\n", line))
	}
}
<|endoftext|>


<commit_before>/*
The quickselect package provides primitives for finding the smallest k elements
in slices and user-defined collections. The primitives used in the package are
modeled off of the standard sort library for Go. Quickselect uses Hoare's
Selection Algorithm which finds the smallest k elements in expected O(n) time,
and is thus an asymptotically optimal algorithm (and is faster than sorting or
heap implementations).
*/
package quickselect

import (
  "math/rand"
  "errors"
  "fmt"
)

/*
A type, typically a collection, which satisfies quickselect.Interface can be
used as data in the QuickSelect method. The interface is the same as the
interface required by Go's canonical sorting package (sort.Interface).

Note that the methods require that the elements of the collection be enumerated
by an integer index.
*/
type Interface interface {
  // Len is the number of elements in the collection
  Len() int
  // Less reports whether the element with
  // index i should sort before the element with index j
  Less(i, j int) bool
  // Swap swaps the order of elements i and j
  Swap(i, j int)
}

type reverse struct {
  // This embedded Interface permits Reverse to use the methods of
  // another Interface implementation.
  Interface
}

// Less returns the opposite of the embedded implementation's Less method.
func (r reverse) Less(i, j int) bool {
  return r.Interface.Less(j, i)
}

func Reverse(data Interface) (Interface) {
  return &reverse{data}
}

// The IntSlice type attaches the QuickSelect interface to an array of ints. It
// implements Interface so that you can call QuickSelect(k) on any IntSlice.
type IntSlice []int

func (t IntSlice) Len() (int) {
  return len(t)
}

func (t IntSlice) Less(i, j int) bool {
  return t[i] < t[j]
}

func (t IntSlice) Swap(i, j int) {
  t[i], t[j] = t[j], t[i]
}

// QuickSelect(k) mutates the IntSlice so that the first k elements in the
// IntSlice are the k smallest elements in the slice. This is a convenience
// method for QuickSelect
func (t IntSlice) QuickSelect(k int) (error) {
  return QuickSelect(t, k)
}

// The Float64Slice type attaches the QuickSelect interface to an array of
// float64s. It implements Interface so that you can call QuickSelect(k) on any
// Float64Slice.
type Float64Slice []float64

func (t Float64Slice) Len() (int) {
  return len(t)
}

func (t Float64Slice) Less(i, j int) bool {
  return t[i] < t[j] || isNaN(t[i]) && !isNaN(t[j])
}

func (t Float64Slice) Swap(i, j int) {
  t[i], t[j] = t[j], t[i]
}

// QuickSelect(k) mutates the Float64Slice so that the first k elements in the
// Float64Slice are the k smallest elements in the slice. This is a convenience
// method for QuickSelect
func (t Float64Slice) QuickSelect(k int) (error) {
  return QuickSelect(t, k)
}

// The StringSlice type attaches the QuickSelect interface to an array of
// float64s. It implements Interface so that you can call QuickSelect(k) on any
// StringSlice.
type StringSlice []string

func (t StringSlice) Len() (int) {
  return len(t)
}

func (t StringSlice) Less(i, j int) bool {
  return t[i] < t[j]
}

func (t StringSlice) Swap(i, j int) {
  t[i], t[j] = t[j], t[i]
}

// QuickSelect(k) mutates the StringSlice so that the first k elements in the
// StringSlice are the k smallest elements in the slice. This is a convenience
// method for QuickSelect
func (t StringSlice) QuickSelect(k int) (error) {
  return QuickSelect(t, k)
}

// isNaN is a copy of math.IsNaN to avoid a dependency on the math package.
func isNaN(f float64) bool {
  return f != f
}

/*
Helper function that does all of the work for QuickSelect. This implements
Hoare's Selection Algorithm which finds the smallest k elements in an interface
in expected O(n) time.

The algorithm works by finding a random pivot element, and making sure all the
elements to the left are less than the pivot element and vice versa for
elements on the right. Recursing on this solves the selection algorithm.
*/
func randomizedMedianFinding(data Interface, low, high, k int) {
  var pivotIndex int

  for {
    if low >= high {
      return
    }
    pivotIndex = rand.Intn(high + 1 - low) + low
    pivotIndex = partition(data, low, high, pivotIndex)

    if k < pivotIndex {
      high = pivotIndex - 1
    } else if k > pivotIndex {
      low = pivotIndex + 1
    } else {
      return
    }
  }
}

/*
Helper function for the selection algorithm. Returns the partitionIndex.

It goes through all elements between low and high and makes sure that the
elements in the range [low, partitionIndex) are less than the element that was
originally in the pivotIndex and that the elements in the range
[paritionIndex + 1, high] are greater than the element originally in the
pivotIndex.
*/
func partition(data Interface, low, high, pivotIndex int) (int) {
  partitionIndex := low
  data.Swap(pivotIndex, high)
  for i := low; i < high; i++ {
    if data.Less(i, high) {
      data.Swap(i, partitionIndex)
      partitionIndex++
    }
  }
  data.Swap(partitionIndex, high)
  return partitionIndex
}

/*
QuickSelect swaps elements in the data provided so that the first k elements
(i.e. the elements occuping indices 0, 1, ..., k-1) are the smallest k elements
in the data.

QuickSelect implements Hoare's Selection Algorithm and runs in O(n) time, so it
is asymptotically faster than sorting or other heap-like implementations for
finding the smallest k elements in a data structure.

Note that k must be in the range [0, data.Len()), otherwise the QuickSelect
method will raise an error.
*/
func QuickSelect(data Interface, k int) (error) {
  length := data.Len()
  if (k < 0 || k >= length) {
    message := fmt.Sprintf("The specified index '%d' is outside of the data's range of indices [0,%d)", k, length)
    return errors.New(message)
  }
  randomizedMedianFinding(data, 0, length - 1, k)
  return nil
}

// IntQuickSelect mutates the data so that the first k elements in the int
// slice are the k smallest elements in the slice. This is a convenience
// method for QuickSelect on int slices.
func IntQuickSelect(data []int, k int) (error) {
  return QuickSelect(IntSlice(data), k)
}

// Float64Select mutates the data so that the first k elements in the float64
// slice are the k smallest elements in the slice. This is a convenience
// method for QuickSelect on float64 slices.
func Float64QuickSelect(data []float64, k int) (error) {
  return QuickSelect(Float64Slice(data), k)
}

// StringQuickSelect mutates the data so that the first k elements in the string
// slice are the k smallest elements in the slice. This is a convenience
// method for QuickSelect on string slices.
func StringQuickSelect(data []string, k int) (error) {
  return QuickSelect(StringSlice(data), k)
}
<commit_msg>Working on improving the performance of quickselect in edge cases.<commit_after>/*
The quickselect package provides primitives for finding the smallest k elements
in slices and user-defined collections. The primitives used in the package are
modeled off of the standard sort library for Go. Quickselect uses Hoare's
Selection Algorithm which finds the smallest k elements in expected O(n) time,
and is thus an asymptotically optimal algorithm (and is faster than sorting or
heap implementations).
*/
package quickselect

import (
  "math/rand"
  "errors"
  "fmt"
)

const (
  partitionThreshold = 8
  randomizedSelectionThreshold = 10
)

/*
A type, typically a collection, which satisfies quickselect.Interface can be
used as data in the QuickSelect method. The interface is the same as the
interface required by Go's canonical sorting package (sort.Interface).

Note that the methods require that the elements of the collection be enumerated
by an integer index.
*/
type Interface interface {
  // Len is the number of elements in the collection
  Len() int
  // Less reports whether the element with
  // index i should sort before the element with index j
  Less(i, j int) bool
  // Swap swaps the order of elements i and j
  Swap(i, j int)
}

type reverse struct {
  // This embedded Interface permits Reverse to use the methods of
  // another Interface implementation.
  Interface
}

// Less returns the opposite of the embedded implementation's Less method.
func (r reverse) Less(i, j int) bool {
  return r.Interface.Less(j, i)
}

func Reverse(data Interface) (Interface) {
  return &reverse{data}
}

// The IntSlice type attaches the QuickSelect interface to an array of ints. It
// implements Interface so that you can call QuickSelect(k) on any IntSlice.
type IntSlice []int

func (t IntSlice) Len() (int) {
  return len(t)
}

func (t IntSlice) Less(i, j int) bool {
  return t[i] < t[j]
}

func (t IntSlice) Swap(i, j int) {
  t[i], t[j] = t[j], t[i]
}

// QuickSelect(k) mutates the IntSlice so that the first k elements in the
// IntSlice are the k smallest elements in the slice. This is a convenience
// method for QuickSelect
func (t IntSlice) QuickSelect(k int) (error) {
  return QuickSelect(t, k)
}

// The Float64Slice type attaches the QuickSelect interface to an array of
// float64s. It implements Interface so that you can call QuickSelect(k) on any
// Float64Slice.
type Float64Slice []float64

func (t Float64Slice) Len() (int) {
  return len(t)
}

func (t Float64Slice) Less(i, j int) bool {
  return t[i] < t[j] || isNaN(t[i]) && !isNaN(t[j])
}

func (t Float64Slice) Swap(i, j int) {
  t[i], t[j] = t[j], t[i]
}

// QuickSelect(k) mutates the Float64Slice so that the first k elements in the
// Float64Slice are the k smallest elements in the slice. This is a convenience
// method for QuickSelect
func (t Float64Slice) QuickSelect(k int) (error) {
  return QuickSelect(t, k)
}

// The StringSlice type attaches the QuickSelect interface to an array of
// float64s. It implements Interface so that you can call QuickSelect(k) on any
// StringSlice.
type StringSlice []string

func (t StringSlice) Len() (int) {
  return len(t)
}

func (t StringSlice) Less(i, j int) bool {
  return t[i] < t[j]
}

func (t StringSlice) Swap(i, j int) {
  t[i], t[j] = t[j], t[i]
}

// QuickSelect(k) mutates the StringSlice so that the first k elements in the
// StringSlice are the k smallest elements in the slice. This is a convenience
// method for QuickSelect
func (t StringSlice) QuickSelect(k int) (error) {
  return QuickSelect(t, k)
}

// isNaN is a copy of math.IsNaN to avoid a dependency on the math package.
func isNaN(f float64) bool {
  return f != f
}

/*
Helper function that does all of the work for QuickSelect. This implements
Hoare's Selection Algorithm which finds the smallest k elements in an interface
in expected O(n) time.

The algorithm works by finding a random pivot element, and making sure all the
elements to the left are less than the pivot element and vice versa for
elements on the right. Recursing on this solves the selection algorithm.
*/
func randomizedSelectionFinding(data Interface, low, high, k int) {
  var pivotIndex int

  for {
    if low >= high {
      return
    } else if high - low <= partitionThreshold {
      insertionSort(data, low, high + 1)
      return
    }

    pivotIndex = rand.Intn(high + 1 - low) + low
    pivotIndex = partition(data, low, high, pivotIndex)

    if k < pivotIndex {
      high = pivotIndex - 1
    } else if k > pivotIndex {
      low = pivotIndex + 1
    } else {
      return
    }
  }
}

// Insertion sort
func insertionSort(data Interface, a, b int) {
  for i := a + 1; i < b; i++ {
    for j := i; j > a && data.Less(j, j-1); j-- {
      data.Swap(j, j-1)
    }
  }
}

// TODO: fix the implementation of this
func naiveSelectionFinding(data Interface, k int) {
  smallestIndices := make([]int, k)
  for i := 0; i < k; i++ {
    smallestIndices[i] = i
  }

  for i := k; i < data.Len(); i++ {
    if data.Less(i, smallestIndices[k-1]) {
      smallestIndices[k-1] = i
      resetLargestIndex(smallestIndices, data)
    }
  }
}

/*
Takes the largest index in `indices` according to the data Interface and places
it at the end of the indices array.
*/
func resetLargestIndex(indices []int, data Interface) {
  var currentSmallest = 0
  for i := 1; i < len(indices); i++ {
    if data.Less(i, currentSmallest) {
      currentSmallest = i
    }
  }

  indices[len(indices) - 1] = currentSmallest
}

/*
Helper function for the selection algorithm. Returns the partitionIndex.

It goes through all elements between low and high and makes sure that the
elements in the range [low, partitionIndex) are less than the element that was
originally in the pivotIndex and that the elements in the range
[paritionIndex + 1, high] are greater than the element originally in the
pivotIndex.
*/
func partition(data Interface, low, high, pivotIndex int) (int) {
  partitionIndex := low
  data.Swap(pivotIndex, high)
  for i := low; i < high; i++ {
    if data.Less(i, high) {
      data.Swap(i, partitionIndex)
      partitionIndex++
    }
  }
  data.Swap(partitionIndex, high)
  return partitionIndex
}

/*
QuickSelect swaps elements in the data provided so that the first k elements
(i.e. the elements occuping indices 0, 1, ..., k-1) are the smallest k elements
in the data.

QuickSelect implements Hoare's Selection Algorithm and runs in O(n) time, so it
is asymptotically faster than sorting or other heap-like implementations for
finding the smallest k elements in a data structure.

Note that k must be in the range [0, data.Len()), otherwise the QuickSelect
method will raise an error.
*/
func QuickSelect(data Interface, k int) (error) {
  length := data.Len()
  if (k < 0 || k >= length) {
    message := fmt.Sprintf("The specified index '%d' is outside of the data's range of indices [0,%d)", k, length)
    return errors.New(message)
  }
  if k > randomizedSelectionThreshold {
    randomizedSelectionFinding(data, 0, length - 1, k)
  } else {
    naiveSelectionFinding(data, k)
  }

  return nil
}

// IntQuickSelect mutates the data so that the first k elements in the int
// slice are the k smallest elements in the slice. This is a convenience
// method for QuickSelect on int slices.
func IntQuickSelect(data []int, k int) (error) {
  return QuickSelect(IntSlice(data), k)
}

// Float64Select mutates the data so that the first k elements in the float64
// slice are the k smallest elements in the slice. This is a convenience
// method for QuickSelect on float64 slices.
func Float64QuickSelect(data []float64, k int) (error) {
  return QuickSelect(Float64Slice(data), k)
}

// StringQuickSelect mutates the data so that the first k elements in the string
// slice are the k smallest elements in the slice. This is a convenience
// method for QuickSelect on string slices.
func StringQuickSelect(data []string, k int) (error) {
  return QuickSelect(StringSlice(data), k)
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"

	"github.com/hashicorp/terraform/helper/schema"
)

// See # http://docs.aws.amazon.com/general/latest/gr/rande.html#elasticbeanstalk_region
var elasticBeanstalkHostedZoneIds = map[string]string{
	"ap-southeast-1": "Z16FZ9L249IFLT",
	"ap-southeast-2": "Z2PCDNR3VC2G1N",
	"ap-northeast-1": "Z1R25G3KIG2GBW",
	"ap-northeast-2": "Z3JE5OI70TWKCP",
	"ap-south-1":     "Z18NTBI3Y7N9TZ",
	"ca-central-1":   "ZJFCZL7SSZB5I",
	"eu-central-1":   "Z1FRNW7UH4DEZJ",
	"eu-west-1":      "Z2NYPWQ7DFZAZH",
	"eu-west-2":      "Z1GKAAAUGATPF1",
	"eu-west-3":      "Z5WN6GAYWG5OB",
	"sa-east-1":      "Z10X7K2B4QSOFV",
	"us-east-1":      "Z117KPS5GTRQ2G",
	"us-east-2":      "Z14LCN19Q5QHIC",
	"us-west-1":      "Z1LQECGX5PH1X",
	"us-west-2":      "Z38NKT9BP95V3O",
}

func dataSourceAwsElasticBeanstalkHostedZone() *schema.Resource {
	return &schema.Resource{
		Read: dataSourceAwsElasticBeanstalkHostedZoneRead,

		Schema: map[string]*schema.Schema{
			"region": {
				Type:     schema.TypeString,
				Optional: true,
			},
		},
	}
}

func dataSourceAwsElasticBeanstalkHostedZoneRead(d *schema.ResourceData, meta interface{}) error {
	region := meta.(*AWSClient).region
	if v, ok := d.GetOk("region"); ok {
		region = v.(string)
	}

	zoneID, ok := elasticBeanstalkHostedZoneIds[region]

	if !ok {
		return fmt.Errorf("Unsupported region: %s", region)
	}

	d.SetId(zoneID)
	d.Set("region", region)
	return nil
}
<commit_msg>data-source/aws_elastic_beanstalk_hosted_zone: Add eu-north-1 region support<commit_after>package aws

import (
	"fmt"

	"github.com/hashicorp/terraform/helper/schema"
)

// See # http://docs.aws.amazon.com/general/latest/gr/rande.html#elasticbeanstalk_region
var elasticBeanstalkHostedZoneIds = map[string]string{
	"ap-southeast-1": "Z16FZ9L249IFLT",
	"ap-southeast-2": "Z2PCDNR3VC2G1N",
	"ap-northeast-1": "Z1R25G3KIG2GBW",
	"ap-northeast-2": "Z3JE5OI70TWKCP",
	"ap-south-1":     "Z18NTBI3Y7N9TZ",
	"ca-central-1":   "ZJFCZL7SSZB5I",
	"eu-central-1":   "Z1FRNW7UH4DEZJ",
	"eu-north-1":     "Z23GO28BZ5AETM",
	"eu-west-1":      "Z2NYPWQ7DFZAZH",
	"eu-west-2":      "Z1GKAAAUGATPF1",
	"eu-west-3":      "Z5WN6GAYWG5OB",
	"sa-east-1":      "Z10X7K2B4QSOFV",
	"us-east-1":      "Z117KPS5GTRQ2G",
	"us-east-2":      "Z14LCN19Q5QHIC",
	"us-west-1":      "Z1LQECGX5PH1X",
	"us-west-2":      "Z38NKT9BP95V3O",
}

func dataSourceAwsElasticBeanstalkHostedZone() *schema.Resource {
	return &schema.Resource{
		Read: dataSourceAwsElasticBeanstalkHostedZoneRead,

		Schema: map[string]*schema.Schema{
			"region": {
				Type:     schema.TypeString,
				Optional: true,
			},
		},
	}
}

func dataSourceAwsElasticBeanstalkHostedZoneRead(d *schema.ResourceData, meta interface{}) error {
	region := meta.(*AWSClient).region
	if v, ok := d.GetOk("region"); ok {
		region = v.(string)
	}

	zoneID, ok := elasticBeanstalkHostedZoneIds[region]

	if !ok {
		return fmt.Errorf("Unsupported region: %s", region)
	}

	d.SetId(zoneID)
	d.Set("region", region)
	return nil
}
<|endoftext|>


<commit_before>package rbtree

import (
	"fmt"
	"testing"
)

func TestNew(t *testing.T) {
	tree := New()
	if tree.String() != "" {
		t.Error("empty tree somehow made a non-empty string")
	}
}

func TestInsert(t *testing.T) {
	tree := New()
	tree.Insert(5, 4, 7, 3, 2, 6, 8, 9)
	fmt.Println(tree)
}

func TestRange(t *testing.T) {
    expected := []int64{5, 4, 7, 3, 2, 6, 8, 9}
	tree := New()
	tree.Insert(expected...)
    for v := range tree.Iterate() {
        fmt.Println(v)
    }
}
<commit_msg>implement tests for rotation, cloning and slicing<commit_after>package rbtree

import (
	"fmt"
	"testing"
)

func TestNew(t *testing.T) {
	tree := New()
	if tree.String() != "" {
		t.Error("empty tree somehow made a non-empty string")
	}
}

func TestInsert(t *testing.T) {
	tree := New()
	tree.Insert(5, 4, 7, 3, 2, 6, 8, 9)
	fmt.Println(tree)
}

func TestRange(t *testing.T) {
    expected := []int64{5, 4, 7, 3, 2, 6, 8, 9}
	tree := New()
	tree.Insert(expected...)
    for v := range tree.Iterate() {
        fmt.Println(v)
    }
}

func TestBasicRotateRight(t *testing.T) {
    expected := []int64{4, 5, 2, 1, 3}
	tree := New()
	tree.Insert(expected...)
    fmt.Println("before: ", tree.String())
    tree.rotateRight(tree.root)
    fmt.Println("after: ", tree.String())
}

func TestBasicRotateLeft(t *testing.T) {
    expected := []int64{2, 1, 4, 3, 5}
	tree := New()
	tree.Insert(expected...)
    fmt.Println("before: ", tree.String())
    tree.rotateLeft(tree.root)
    fmt.Println("after: ", tree.String())
}

func TestSmallClone(t *testing.T) {
    expected := []int64{4, 5, 2, 1, 3}
	tree := New()
	tree.Insert(expected...)
    newTree := tree.Clone()
    if tree.String() != newTree.String() {
		t.Error("Expected ", tree.String(), " got ", newTree.String())
    }
}

func TestSmallSlice(t *testing.T) {
    values := []int64{4, 5, 2, 1, 3}
    //expected:= []int64{4, 5, 2, 1, 3}
    tree := New()
    tree.Insert(values...)
    slice := tree.Slice()
    fmt.Println(slice)
}
<|endoftext|>


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

package size

import (
	"reflect"
)

func init() {
	var a bool
	SizeOfBool = int(reflect.TypeOf(a).Size())
	var b float32
	SizeOfFloat32 = int(reflect.TypeOf(b).Size())
	var c float64
	SizeOfFloat64 = int(reflect.TypeOf(c).Size())
	var d map[int]int
	SizeOfMap = int(reflect.TypeOf(d).Size())
	var e *int
	SizeOfPtr = int(reflect.TypeOf(e).Size())
	var f []int
	SizeOfSlice = int(reflect.TypeOf(f).Size())
	var g string
	SizeOfString = int(reflect.TypeOf(g).Size())
	var h uint8
	SizeOfUint8 = int(reflect.TypeOf(h).Size())
	var i uint16
	SizeOfUint16 = int(reflect.TypeOf(i).Size())
	var j uint32
	SizeOfUint32 = int(reflect.TypeOf(j).Size())
	var k uint64
	SizeOfUint64 = int(reflect.TypeOf(k).Size())
}

var SizeOfBool int
var SizeOfFloat32 int
var SizeOfFloat64 int
var SizeOfInt int
var SizeOfMap int
var SizeOfPtr int
var SizeOfSlice int
var SizeOfString int
var SizeOfUint8 int
var SizeOfUint16 int
var SizeOfUint32 int
var SizeOfUint64 int
<commit_msg>Include missing initialization for size entry of integers<commit_after>//  Copyright (c) 2018 Couchbase, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 		http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package size

import (
	"reflect"
)

func init() {
	var b bool
	SizeOfBool = int(reflect.TypeOf(b).Size())
	var f32 float32
	SizeOfFloat32 = int(reflect.TypeOf(f32).Size())
	var f64 float64
	SizeOfFloat64 = int(reflect.TypeOf(f64).Size())
	var i int
	SizeOfInt = int(reflect.TypeOf(i).Size())
	var m map[int]int
	SizeOfMap = int(reflect.TypeOf(m).Size())
	var ptr *int
	SizeOfPtr = int(reflect.TypeOf(ptr).Size())
	var slice []int
	SizeOfSlice = int(reflect.TypeOf(slice).Size())
	var str string
	SizeOfString = int(reflect.TypeOf(str).Size())
	var u8 uint8
	SizeOfUint8 = int(reflect.TypeOf(u8).Size())
	var u16 uint16
	SizeOfUint16 = int(reflect.TypeOf(u16).Size())
	var u32 uint32
	SizeOfUint32 = int(reflect.TypeOf(u32).Size())
	var u64 uint64
	SizeOfUint64 = int(reflect.TypeOf(u64).Size())
}

var SizeOfBool int
var SizeOfFloat32 int
var SizeOfFloat64 int
var SizeOfInt int
var SizeOfMap int
var SizeOfPtr int
var SizeOfSlice int
var SizeOfString int
var SizeOfUint8 int
var SizeOfUint16 int
var SizeOfUint32 int
var SizeOfUint64 int
<|endoftext|>


<commit_before>package fit_test

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/tormoder/fit"
)

var (
	update  = flag.Bool("update", false, "update .golden output and table for decode test files if their fingerprint differs")
	fupdate = flag.Bool("fupdate", false, "force regeneration of decode test files table")
	fdecode = flag.Bool("fdecode", false, "force decode golden part of decode test irregardless of Go version")
)

var (
	activitySmallMu   sync.Mutex
	activitySmallOnce sync.Once
	activitySmallData []byte
)

func activitySmall() []byte {
	activitySmallMu.Lock()
	defer activitySmallMu.Unlock()
	activitySmallOnce.Do(func() {
		asd, err := ioutil.ReadFile(activitySmallPath)
		if err != nil {
			errDesc := fmt.Sprintf("parseActivitySmallData failed: %v", err)
			panic(errDesc)
		}
		activitySmallData = asd
	})
	return activitySmallData
}

var (
	activitySmallPath      = filepath.Join(tdfolder, "me", "activity-small-fenix2-run.fit")
	activityLargePath      = filepath.Join(tdfolder, "me", "activity-large-fenxi2-multisport.fit")
	activityComponentsPath = filepath.Join(tdfolder, "dcrainmaker", "Edge810-Vector-2013-08-16-15-35-10.fit")
	monitoringPath         = filepath.Join(tdfolder, "fitsdk", "MonitoringFile.fit")
)

const (
	goldenSuffix  = ".golden"
	currentSuffix = ".current"
	gzSuffix      = ".gz"
	tdfolder      = "testdata"
)

func TestMain(m *testing.M) {
	flag.Parse()
	os.Exit(m.Run())
}

func TestDecode(t *testing.T) {
	const goMajorVersionForDecodeGolden = "go1.11"
	testDecodeGolden := true
	goVersion := runtime.Version()
	goVersionOK := strings.HasPrefix(goVersion, goMajorVersionForDecodeGolden)
	switch {
	case !goVersionOK && !*fdecode:
		testDecodeGolden = false
		t.Logf(
			"skipping golden decode part of test due to Go version (enabled for %s.x, have %q)",
			goMajorVersionForDecodeGolden,
			goVersion,
		)
	case !goVersionOK && *fdecode:
		t.Logf(
			"override: performing golden decode part of test for Go version %q (default only for %s.x)",
			goVersion,
			goMajorVersionForDecodeGolden,
		)
	default:
	}

	regenTestTable := struct {
		sync.Mutex // Protects val and decodeTestFiles slice in reader_util_test.go.
		val        bool
	}{}

	t.Run("Group", func(t *testing.T) {
		for i, file := range decodeTestFiles {
			i, file := i, file // Capture range variables.
			t.Run(fmt.Sprintf("%s/%s", file.folder, file.name), func(t *testing.T) {
				t.Parallel()
				fpath := filepath.Join(tdfolder, file.folder, file.name)
				data, err := ioutil.ReadFile(fpath)
				if err != nil {
					t.Fatalf("reading file failed: %v", err)
				}
				fitFile, err := fit.Decode(bytes.NewReader(data), file.dopts.opts()...)
				if !file.wantErr && err != nil {
					t.Fatalf("got error, want none; error is: %v", err)
				}
				if file.wantErr && err == nil {
					t.Fatalf("got no error, want error")
				}
				if !testDecodeGolden {
					return
				}
				if file.fingerprint == 0 {
					return
				}
				fp := fitFingerprint(fitFile)
				if fp == file.fingerprint {
					return
				}
				t.Errorf("fit file fingerprint differs: got: %d, want: %d", fp, file.fingerprint)
				if !*update {
					fpath = fpath + currentSuffix
				} else {
					fpath = fpath + goldenSuffix
				}
				if file.compress {
					fpath = fpath + gzSuffix
				}
				err = fitUtterDump(fitFile, fpath, file.compress)
				if err != nil {
					t.Fatalf("error writing output: %v", err)
				}
				if !*update {
					t.Logf("current output written to: %s", fpath)
					t.Logf("use a diff tool to compare (e.g. zdiff if compressed)")
				} else {
					regenTestTable.Lock()
					regenTestTable.val = true
					decodeTestFiles[i].fingerprint = fp
					regenTestTable.Unlock()
					t.Logf("%q has been updated", fpath)
					t.Logf("new fingerprint is: %d, update test case in reader_test.go", fp)
				}
			})
		}
	})

	if regenTestTable.val || *fupdate {
		t.Logf("regenerating table for decode test files...")
		err := regenerateDecodeTestTable()
		if err != nil {
			t.Fatalf("error regenerating table for decode test files: %v", err)
		}
	}
}

func TestDecodeChained(t *testing.T) {
	chainedTestFiles := []struct {
		fpath   string
		dfiles  int
		wantErr bool
		desc    string
	}{
		{
			filepath.Join(tdfolder, "fitsdk", "Activity.fit"),
			1,
			false,
			"single valid fit file",
		},
		{
			filepath.Join(tdfolder, "chained", "activity-settings.fit"),
			2,
			false,
			"two valid chained fit files",
		},
		{
			filepath.Join(tdfolder, "chained", "activity-activity-filecrc.fit"),
			2,
			true,
			"one valid fit file + one fit file with wrong crc",
		},
		{
			filepath.Join(tdfolder, "chained", "activity-settings-corruptheader.fit"),
			1,
			true,
			"one valid fit file + one fit file with corrupt header",
		},
		{
			filepath.Join(tdfolder, "chained", "activity-settings-nodata.fit"),
			2,
			true,
			"one valid fit file + one fit file with ok header but no data",
		},
	}

	for _, ctf := range chainedTestFiles {
		ctf := ctf
		t.Run(ctf.fpath, func(t *testing.T) {
			t.Parallel()
			data, err := ioutil.ReadFile(ctf.fpath)
			if err != nil {
				t.Fatalf("reading file data failed: %v", err)
			}
			fitFiles, err := fit.DecodeChained(bytes.NewReader(data))
			if !ctf.wantErr && err != nil {
				t.Fatalf("got error, want none; error is: %v", err)
			}
			if ctf.wantErr && err == nil {
				t.Fatalf("got no error, want error")
			}
			if len(fitFiles) != ctf.dfiles {
				t.Fatalf("got %d decoded fit file(s), want %d", len(fitFiles), ctf.dfiles)
			}
		})
	}
}

func TestCheckIntegrity(t *testing.T) {
	t.Run("ActivitySmall", func(t *testing.T) {
		err := fit.CheckIntegrity(bytes.NewReader(activitySmall()), false)
		if err != nil {
			t.Errorf("%q: failed: %v", activitySmallPath, err)
		}
	})
	t.Run("ActivitySDK", func(t *testing.T) {
		fpath := filepath.Join(tdfolder, "fitsdk", "Activity.fit")
		data, err := ioutil.ReadFile(fpath)
		if err != nil {
			t.Fatalf("reading %q failed: %v", fpath, err)
		}
		err = fit.CheckIntegrity(bytes.NewReader(data), false)
		if err != nil {
			t.Errorf("%q: failed: %v", fpath, err)
		}
	})
}

func TestDecodeHeader(t *testing.T) {
	wantHeader := fit.Header{
		Size:            0xe,
		ProtocolVersion: 0x10,
		ProfileVersion:  0x457,
		DataSize:        0x1dbdf,
		DataType:        [4]uint8{0x2e, 0x46, 0x49, 0x54},
		CRC:             0x1ec4,
	}
	gotHeader, err := fit.DecodeHeader(bytes.NewReader(activitySmall()))
	if err != nil {
		t.Errorf("%q: failed: %v", activitySmallPath, err)
	}
	if gotHeader != wantHeader {
		t.Errorf("got header:\n%#v\nwant header: %#v", gotHeader, wantHeader)
	}
}

func TestDecodeHeaderAndFileID(t *testing.T) {
	wantHeader := fit.Header{
		Size:            0xe,
		ProtocolVersion: 0x10,
		ProfileVersion:  0x457,
		DataSize:        0x1dbdf,
		DataType:        [4]uint8{0x2e, 0x46, 0x49, 0x54},
		CRC:             0x1ec4,
	}
	tc := time.Unix(1439652761, 0)
	tc = tc.UTC()
	wantFileId := fit.FileIdMsg{
		Type:         0x4,
		Manufacturer: 0x1,
		Product:      0x7af,
		SerialNumber: 0xe762d9cf,
		Number:       0xffff,
		TimeCreated:  tc,
		ProductName:  "",
	}

	gotHeader, gotFileId, err := fit.DecodeHeaderAndFileID(bytes.NewReader(activitySmall()))
	if err != nil {
		t.Errorf("%q: failed: %v", activitySmallPath, err)
	}
	if gotHeader != wantHeader {
		t.Errorf("%q:\ngot header:\n%#v\nwant header:\n%#v", activitySmallPath, gotHeader, wantHeader)
	}
	if gotFileId != wantFileId {
		t.Errorf("%q:\ngot FileIdMsg:\n%v\nwant FileIdMsg:\n%v", activitySmallPath, gotFileId, wantFileId)
	}
}

func BenchmarkDecode(b *testing.B) {
	files := []struct {
		desc, path string
	}{
		{"ActivitySmall", activitySmallPath},
		{"ActivityLarge", activityLargePath},
		{"ActivityWithComponents", activityComponentsPath},
		{"MonitoringFile", monitoringPath},
	}
	for _, file := range files {
		b.Run(file.desc, func(b *testing.B) {
			data, err := ioutil.ReadFile(file.path)
			if err != nil {
				b.Fatalf("%q: error reading file: %v", file.path, err)
			}
			b.ReportAllocs()
			b.SetBytes(int64(len(data)))
			b.ResetTimer()
			for i := 0; i < b.N; i++ {
				_, err := fit.Decode(bytes.NewReader(data))
				if err != nil {
					b.Fatalf("%q: error decoding file: %v", file.path, err)
				}
			}
		})
	}
}

func BenchmarkDecodeActivityLargeParallel(b *testing.B) {
	data, err := ioutil.ReadFile(activityLargePath)
	if err != nil {
		b.Fatal(err)
	}
	b.ReportAllocs()
	b.SetBytes(int64(len(data)))
	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			_, err := fit.Decode(bytes.NewReader(data))
			if err != nil {
				b.Fatal(err)
			}
		}
	})
}

func BenchmarkDecodeHeader(b *testing.B) {
	data := activitySmall()
	b.ReportAllocs()
	b.SetBytes(int64(len(data)))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := fit.DecodeHeader(bytes.NewReader(data))
		if err != nil {
			b.Fatalf("%q: error decoding header: %v", activitySmallPath, err)
		}
	}

}

func BenchmarkDecodeHeaderAndFileID(b *testing.B) {
	data := activitySmall()
	b.ReportAllocs()
	b.SetBytes(int64(len(data)))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, _, err := fit.DecodeHeaderAndFileID(bytes.NewReader(data))
		if err != nil {
			b.Fatalf("%q: error decoding header/fileid: %v", activitySmallPath, err)
		}
	}
}
<commit_msg>reader_test: bump Go version for decode tests to 1.12<commit_after>package fit_test

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/tormoder/fit"
)

var (
	update  = flag.Bool("update", false, "update .golden output and table for decode test files if their fingerprint differs")
	fupdate = flag.Bool("fupdate", false, "force regeneration of decode test files table")
	fdecode = flag.Bool("fdecode", false, "force decode golden part of decode test irregardless of Go version")
)

var (
	activitySmallMu   sync.Mutex
	activitySmallOnce sync.Once
	activitySmallData []byte
)

func activitySmall() []byte {
	activitySmallMu.Lock()
	defer activitySmallMu.Unlock()
	activitySmallOnce.Do(func() {
		asd, err := ioutil.ReadFile(activitySmallPath)
		if err != nil {
			errDesc := fmt.Sprintf("parseActivitySmallData failed: %v", err)
			panic(errDesc)
		}
		activitySmallData = asd
	})
	return activitySmallData
}

var (
	activitySmallPath      = filepath.Join(tdfolder, "me", "activity-small-fenix2-run.fit")
	activityLargePath      = filepath.Join(tdfolder, "me", "activity-large-fenxi2-multisport.fit")
	activityComponentsPath = filepath.Join(tdfolder, "dcrainmaker", "Edge810-Vector-2013-08-16-15-35-10.fit")
	monitoringPath         = filepath.Join(tdfolder, "fitsdk", "MonitoringFile.fit")
)

const (
	goldenSuffix  = ".golden"
	currentSuffix = ".current"
	gzSuffix      = ".gz"
	tdfolder      = "testdata"
)

func TestMain(m *testing.M) {
	flag.Parse()
	os.Exit(m.Run())
}

func TestDecode(t *testing.T) {
	const goMajorVersionForDecodeGolden = "go1.12"
	testDecodeGolden := true
	goVersion := runtime.Version()
	goVersionOK := strings.HasPrefix(goVersion, goMajorVersionForDecodeGolden)
	switch {
	case !goVersionOK && !*fdecode:
		testDecodeGolden = false
		t.Logf(
			"skipping golden decode part of test due to Go version (enabled for %s.x, have %q)",
			goMajorVersionForDecodeGolden,
			goVersion,
		)
	case !goVersionOK && *fdecode:
		t.Logf(
			"override: performing golden decode part of test for Go version %q (default only for %s.x)",
			goVersion,
			goMajorVersionForDecodeGolden,
		)
	default:
	}

	regenTestTable := struct {
		sync.Mutex // Protects val and decodeTestFiles slice in reader_util_test.go.
		val        bool
	}{}

	t.Run("Group", func(t *testing.T) {
		for i, file := range decodeTestFiles {
			i, file := i, file // Capture range variables.
			t.Run(fmt.Sprintf("%s/%s", file.folder, file.name), func(t *testing.T) {
				t.Parallel()
				fpath := filepath.Join(tdfolder, file.folder, file.name)
				data, err := ioutil.ReadFile(fpath)
				if err != nil {
					t.Fatalf("reading file failed: %v", err)
				}
				fitFile, err := fit.Decode(bytes.NewReader(data), file.dopts.opts()...)
				if !file.wantErr && err != nil {
					t.Fatalf("got error, want none; error is: %v", err)
				}
				if file.wantErr && err == nil {
					t.Fatalf("got no error, want error")
				}
				if !testDecodeGolden {
					return
				}
				if file.fingerprint == 0 {
					return
				}
				fp := fitFingerprint(fitFile)
				if fp == file.fingerprint {
					return
				}
				t.Errorf("fit file fingerprint differs: got: %d, want: %d", fp, file.fingerprint)
				if !*update {
					fpath = fpath + currentSuffix
				} else {
					fpath = fpath + goldenSuffix
				}
				if file.compress {
					fpath = fpath + gzSuffix
				}
				err = fitUtterDump(fitFile, fpath, file.compress)
				if err != nil {
					t.Fatalf("error writing output: %v", err)
				}
				if !*update {
					t.Logf("current output written to: %s", fpath)
					t.Logf("use a diff tool to compare (e.g. zdiff if compressed)")
				} else {
					regenTestTable.Lock()
					regenTestTable.val = true
					decodeTestFiles[i].fingerprint = fp
					regenTestTable.Unlock()
					t.Logf("%q has been updated", fpath)
					t.Logf("new fingerprint is: %d, update test case in reader_test.go", fp)
				}
			})
		}
	})

	if regenTestTable.val || *fupdate {
		t.Logf("regenerating table for decode test files...")
		err := regenerateDecodeTestTable()
		if err != nil {
			t.Fatalf("error regenerating table for decode test files: %v", err)
		}
	}
}

func TestDecodeChained(t *testing.T) {
	chainedTestFiles := []struct {
		fpath   string
		dfiles  int
		wantErr bool
		desc    string
	}{
		{
			filepath.Join(tdfolder, "fitsdk", "Activity.fit"),
			1,
			false,
			"single valid fit file",
		},
		{
			filepath.Join(tdfolder, "chained", "activity-settings.fit"),
			2,
			false,
			"two valid chained fit files",
		},
		{
			filepath.Join(tdfolder, "chained", "activity-activity-filecrc.fit"),
			2,
			true,
			"one valid fit file + one fit file with wrong crc",
		},
		{
			filepath.Join(tdfolder, "chained", "activity-settings-corruptheader.fit"),
			1,
			true,
			"one valid fit file + one fit file with corrupt header",
		},
		{
			filepath.Join(tdfolder, "chained", "activity-settings-nodata.fit"),
			2,
			true,
			"one valid fit file + one fit file with ok header but no data",
		},
	}

	for _, ctf := range chainedTestFiles {
		ctf := ctf
		t.Run(ctf.fpath, func(t *testing.T) {
			t.Parallel()
			data, err := ioutil.ReadFile(ctf.fpath)
			if err != nil {
				t.Fatalf("reading file data failed: %v", err)
			}
			fitFiles, err := fit.DecodeChained(bytes.NewReader(data))
			if !ctf.wantErr && err != nil {
				t.Fatalf("got error, want none; error is: %v", err)
			}
			if ctf.wantErr && err == nil {
				t.Fatalf("got no error, want error")
			}
			if len(fitFiles) != ctf.dfiles {
				t.Fatalf("got %d decoded fit file(s), want %d", len(fitFiles), ctf.dfiles)
			}
		})
	}
}

func TestCheckIntegrity(t *testing.T) {
	t.Run("ActivitySmall", func(t *testing.T) {
		err := fit.CheckIntegrity(bytes.NewReader(activitySmall()), false)
		if err != nil {
			t.Errorf("%q: failed: %v", activitySmallPath, err)
		}
	})
	t.Run("ActivitySDK", func(t *testing.T) {
		fpath := filepath.Join(tdfolder, "fitsdk", "Activity.fit")
		data, err := ioutil.ReadFile(fpath)
		if err != nil {
			t.Fatalf("reading %q failed: %v", fpath, err)
		}
		err = fit.CheckIntegrity(bytes.NewReader(data), false)
		if err != nil {
			t.Errorf("%q: failed: %v", fpath, err)
		}
	})
}

func TestDecodeHeader(t *testing.T) {
	wantHeader := fit.Header{
		Size:            0xe,
		ProtocolVersion: 0x10,
		ProfileVersion:  0x457,
		DataSize:        0x1dbdf,
		DataType:        [4]uint8{0x2e, 0x46, 0x49, 0x54},
		CRC:             0x1ec4,
	}
	gotHeader, err := fit.DecodeHeader(bytes.NewReader(activitySmall()))
	if err != nil {
		t.Errorf("%q: failed: %v", activitySmallPath, err)
	}
	if gotHeader != wantHeader {
		t.Errorf("got header:\n%#v\nwant header: %#v", gotHeader, wantHeader)
	}
}

func TestDecodeHeaderAndFileID(t *testing.T) {
	wantHeader := fit.Header{
		Size:            0xe,
		ProtocolVersion: 0x10,
		ProfileVersion:  0x457,
		DataSize:        0x1dbdf,
		DataType:        [4]uint8{0x2e, 0x46, 0x49, 0x54},
		CRC:             0x1ec4,
	}
	tc := time.Unix(1439652761, 0)
	tc = tc.UTC()
	wantFileId := fit.FileIdMsg{
		Type:         0x4,
		Manufacturer: 0x1,
		Product:      0x7af,
		SerialNumber: 0xe762d9cf,
		Number:       0xffff,
		TimeCreated:  tc,
		ProductName:  "",
	}

	gotHeader, gotFileId, err := fit.DecodeHeaderAndFileID(bytes.NewReader(activitySmall()))
	if err != nil {
		t.Errorf("%q: failed: %v", activitySmallPath, err)
	}
	if gotHeader != wantHeader {
		t.Errorf("%q:\ngot header:\n%#v\nwant header:\n%#v", activitySmallPath, gotHeader, wantHeader)
	}
	if gotFileId != wantFileId {
		t.Errorf("%q:\ngot FileIdMsg:\n%v\nwant FileIdMsg:\n%v", activitySmallPath, gotFileId, wantFileId)
	}
}

func BenchmarkDecode(b *testing.B) {
	files := []struct {
		desc, path string
	}{
		{"ActivitySmall", activitySmallPath},
		{"ActivityLarge", activityLargePath},
		{"ActivityWithComponents", activityComponentsPath},
		{"MonitoringFile", monitoringPath},
	}
	for _, file := range files {
		b.Run(file.desc, func(b *testing.B) {
			data, err := ioutil.ReadFile(file.path)
			if err != nil {
				b.Fatalf("%q: error reading file: %v", file.path, err)
			}
			b.ReportAllocs()
			b.SetBytes(int64(len(data)))
			b.ResetTimer()
			for i := 0; i < b.N; i++ {
				_, err := fit.Decode(bytes.NewReader(data))
				if err != nil {
					b.Fatalf("%q: error decoding file: %v", file.path, err)
				}
			}
		})
	}
}

func BenchmarkDecodeActivityLargeParallel(b *testing.B) {
	data, err := ioutil.ReadFile(activityLargePath)
	if err != nil {
		b.Fatal(err)
	}
	b.ReportAllocs()
	b.SetBytes(int64(len(data)))
	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			_, err := fit.Decode(bytes.NewReader(data))
			if err != nil {
				b.Fatal(err)
			}
		}
	})
}

func BenchmarkDecodeHeader(b *testing.B) {
	data := activitySmall()
	b.ReportAllocs()
	b.SetBytes(int64(len(data)))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := fit.DecodeHeader(bytes.NewReader(data))
		if err != nil {
			b.Fatalf("%q: error decoding header: %v", activitySmallPath, err)
		}
	}

}

func BenchmarkDecodeHeaderAndFileID(b *testing.B) {
	data := activitySmall()
	b.ReportAllocs()
	b.SetBytes(int64(len(data)))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, _, err := fit.DecodeHeaderAndFileID(bytes.NewReader(data))
		if err != nil {
			b.Fatalf("%q: error decoding header/fileid: %v", activitySmallPath, err)
		}
	}
}
<|endoftext|>


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

package tcp_test

import (
	"math/rand"
	"net"
	"os"
	"runtime"
	"testing"

	"github.com/mikioh/tcp"
)

func TestOptionWithVariousBufferLenghts(t *testing.T) {
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer ln.Close()
	c, err := net.Dial(ln.Addr().Network(), ln.Addr().String())
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()

	tc, err := tcp.NewConn(c)
	if err != nil {
		t.Fatal(err)
	}
	for i := 0; i < 256; i++ {
		level, name := rand.Int(), rand.Int()
		b := make([]byte, i)
		tc.Option(level, name, b)
	}
}

func TestOriginalDst(t *testing.T) {
	switch runtime.GOOS {
	case "linux":
	case "darwin", "dragonfly", "freebsd", "openbsd":
		if os.Getuid() != 0 {
			t.Skip("must be root")
		}
	default:
		t.Skipf("not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
	}

	for _, address := range []string{"127.0.0.1:0"} {
		ln, err := net.Listen("tcp", address)
		if err != nil {
			t.Fatal(err)
		}
		defer ln.Close()
		c, err := net.Dial(ln.Addr().Network(), ln.Addr().String())
		if err != nil {
			t.Fatal(err)
		}
		defer c.Close()

		tc, err := tcp.NewConn(c)
		if err != nil {
			t.Fatal(err)
		}
		if _, err := tc.OriginalDst(); err != nil {
			t.Fatal(err)
		}
	}
}
<commit_msg>tcp: fix a typo<commit_after>// Copyright 2016 Mikio Hara. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tcp_test

import (
	"math/rand"
	"net"
	"os"
	"runtime"
	"testing"

	"github.com/mikioh/tcp"
)

func TestOptionWithVariousBufferLengths(t *testing.T) {
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer ln.Close()
	c, err := net.Dial(ln.Addr().Network(), ln.Addr().String())
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()

	tc, err := tcp.NewConn(c)
	if err != nil {
		t.Fatal(err)
	}
	for i := 0; i < 256; i++ {
		level, name := rand.Int(), rand.Int()
		b := make([]byte, i)
		tc.Option(level, name, b)
	}
}

func TestOriginalDst(t *testing.T) {
	switch runtime.GOOS {
	case "linux":
	case "darwin", "dragonfly", "freebsd", "openbsd":
		if os.Getuid() != 0 {
			t.Skip("must be root")
		}
	default:
		t.Skipf("not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
	}

	for _, address := range []string{"127.0.0.1:0"} {
		ln, err := net.Listen("tcp", address)
		if err != nil {
			t.Fatal(err)
		}
		defer ln.Close()
		c, err := net.Dial(ln.Addr().Network(), ln.Addr().String())
		if err != nil {
			t.Fatal(err)
		}
		defer c.Close()

		tc, err := tcp.NewConn(c)
		if err != nil {
			t.Fatal(err)
		}
		if _, err := tc.OriginalDst(); err != nil {
			t.Fatal(err)
		}
	}
}
<|endoftext|>


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

package imports

import (
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"

	"code.google.com/p/go.tools/astutil"
)

// importToGroup is a list of functions which map from an import path to
// a group number.
var importToGroup = []func(importPath string) (num int, ok bool){
	func(importPath string) (num int, ok bool) {
		if strings.HasPrefix(importPath, "appengine") {
			return 2, true
		}
		return
	},
	func(importPath string) (num int, ok bool) {
		if strings.Contains(importPath, ".") {
			return 1, true
		}
		return
	},
}

func importGroup(importPath string) int {
	for _, fn := range importToGroup {
		if n, ok := fn(importPath); ok {
			return n
		}
	}
	return 0
}

func fixImports(fset *token.FileSet, f *ast.File) (added []string, err error) {
	// refs are a set of possible package references currently unsatisified by imports.
	// first key: either base package (e.g. "fmt") or renamed package
	// second key: referenced package symbol (e.g. "Println")
	refs := make(map[string]map[string]bool)

	// decls are the current package imports. key is base package or renamed package.
	decls := make(map[string]*ast.ImportSpec)

	// collect potential uses of packages.
	var visitor visitFn
	visitor = visitFn(func(node ast.Node) ast.Visitor {
		if node == nil {
			return visitor
		}
		switch v := node.(type) {
		case *ast.ImportSpec:
			if v.Name != nil {
				decls[v.Name.Name] = v
			} else {
				local := importPathToName(strings.Trim(v.Path.Value, `\"`))
				decls[local] = v
			}
		case *ast.SelectorExpr:
			xident, ok := v.X.(*ast.Ident)
			if !ok {
				break
			}
			if xident.Obj != nil {
				// if the parser can resolve it, it's not a package ref
				break
			}
			pkgName := xident.Name
			if refs[pkgName] == nil {
				refs[pkgName] = make(map[string]bool)
			}
			if decls[pkgName] == nil {
				refs[pkgName][v.Sel.Name] = true
			}
		}
		return visitor
	})
	ast.Walk(visitor, f)

	// Search for imports matching potential package references.
	searches := 0
	type result struct {
		ipath string
		name  string
		err   error
	}
	results := make(chan result)
	for pkgName, symbols := range refs {
		if len(symbols) == 0 {
			continue // skip over packages already imported
		}
		go func(pkgName string, symbols map[string]bool) {
			ipath, rename, err := findImport(pkgName, symbols)
			r := result{ipath: ipath, err: err}
			if rename {
				r.name = pkgName
			}
			results <- r
		}(pkgName, symbols)
		searches++
	}
	for i := 0; i < searches; i++ {
		result := <-results
		if result.err != nil {
			return nil, result.err
		}
		if result.ipath != "" {
			if result.name != "" {
				astutil.AddNamedImport(fset, f, result.name, result.ipath)
			} else {
				astutil.AddImport(fset, f, result.ipath)
			}
			added = append(added, result.ipath)
		}
	}

	// Nil out any unused ImportSpecs, to be removed in following passes
	unusedImport := map[string]bool{}
	for pkg, is := range decls {
		if refs[pkg] == nil && pkg != "_" && pkg != "." {
			unusedImport[strings.Trim(is.Path.Value, `"`)] = true
		}
	}
	for ipath := range unusedImport {
		if ipath == "C" {
			// Don't remove cgo stuff.
			continue
		}
		astutil.DeleteImport(fset, f, ipath)
	}

	return added, nil
}

// importPathToName returns the package name for the given import path.
var importPathToName = importPathToNameGoPath

// importPathToNameBasic assumes the package name is the base of import path.
func importPathToNameBasic(importPath string) (packageName string) {
	return path.Base(importPath)
}

// importPathToNameGoPath finds out the actual package name, as declared in its .go files.
// If there's a problem, it falls back to using importPathToNameBasic.
func importPathToNameGoPath(importPath string) (packageName string) {
	if buildPkg, err := build.Import(importPath, "", 0); err == nil {
		return buildPkg.Name
	} else {
		return importPathToNameBasic(importPath)
	}
}

type pkg struct {
	importpath string // full pkg import path, e.g. "net/http"
	dir        string // absolute file path to pkg directory e.g. "/usr/lib/go/src/fmt"
}

var pkgIndexOnce sync.Once

var pkgIndex struct {
	sync.Mutex
	m map[string][]pkg // shortname => []pkg, e.g "http" => "net/http"
}

func loadPkgIndex() {
	pkgIndex.Lock()
	pkgIndex.m = make(map[string][]pkg)
	pkgIndex.Unlock()

	var wg sync.WaitGroup
	for _, path := range build.Default.SrcDirs() {
		f, err := os.Open(path)
		if err != nil {
			fmt.Fprint(os.Stderr, err)
			continue
		}
		children, err := f.Readdir(-1)
		f.Close()
		if err != nil {
			fmt.Fprint(os.Stderr, err)
			continue
		}
		for _, child := range children {
			if child.IsDir() {
				wg.Add(1)
				go func(path, name string) {
					defer wg.Done()
					loadPkg(&wg, path, name)
				}(path, child.Name())
			}
		}
	}
	wg.Wait()
}

func loadPkg(wg *sync.WaitGroup, root, pkgrelpath string) {
	importpath := filepath.ToSlash(pkgrelpath)
	shortName := importPathToName(importpath)

	dir := filepath.Join(root, importpath)
	pkgIndex.Lock()
	pkgIndex.m[shortName] = append(pkgIndex.m[shortName], pkg{
		importpath: importpath,
		dir:        dir,
	})
	pkgIndex.Unlock()

	pkgDir, err := os.Open(dir)
	if err != nil {
		return
	}
	children, err := pkgDir.Readdir(-1)
	pkgDir.Close()
	if err != nil {
		return
	}
	for _, child := range children {
		name := child.Name()
		if name == "" {
			continue
		}
		if c := name[0]; c == '.' || ('0' <= c && c <= '9') {
			continue
		}
		if child.IsDir() {
			wg.Add(1)
			go func(root, name string) {
				defer wg.Done()
				loadPkg(wg, root, name)
			}(root, filepath.Join(importpath, name))
		}
	}
}

// loadExports returns a list exports for a package.
var loadExports = loadExportsGoPath

func loadExportsGoPath(dir string) map[string]bool {
	exports := make(map[string]bool)
	buildPkg, err := build.ImportDir(dir, 0)
	if err != nil {
		if strings.Contains(err.Error(), "no buildable Go source files in") {
			return nil
		}
		fmt.Fprintf(os.Stderr, "could not import %q: %v", dir, err)
		return nil
	}
	fset := token.NewFileSet()
	for _, file := range buildPkg.GoFiles {
		f, err := parser.ParseFile(fset, filepath.Join(dir, file), nil, 0)
		if err != nil {
			fmt.Fprintf(os.Stderr, "could not parse %q: %v", file, err)
			continue
		}
		for name := range f.Scope.Objects {
			if ast.IsExported(name) {
				exports[name] = true
			}
		}
	}
	return exports
}

// findImport searches for a package with the given symbols.
// If no package is found, findImport returns "".
// Declared as a variable rather than a function so goimports can be easily
// extended by adding a file with an init function.
var findImport = findImportGoPath

func findImportGoPath(pkgName string, symbols map[string]bool) (string, bool, error) {
	// Fast path for the standard library.
	// In the common case we hopefully never have to scan the GOPATH, which can
	// be slow with moving disks.
	if pkg, rename, ok := findImportStdlib(pkgName, symbols); ok {
		return pkg, rename, nil
	}

	// TODO(sameer): look at the import lines for other Go files in the
	// local directory, since the user is likely to import the same packages
	// in the current Go file.  Return rename=true when the other Go files
	// use a renamed package that's also used in the current file.

	pkgIndexOnce.Do(loadPkgIndex)

	// Collect exports for packages with matching names.
	var wg sync.WaitGroup
	var pkgsMu sync.Mutex // guards pkgs
	// full importpath => exported symbol => True
	// e.g. "net/http" => "Client" => True
	pkgs := make(map[string]map[string]bool)
	pkgIndex.Lock()
	for _, pkg := range pkgIndex.m[pkgName] {
		wg.Add(1)
		go func(importpath, dir string) {
			defer wg.Done()
			exports := loadExports(dir)
			if exports != nil {
				pkgsMu.Lock()
				pkgs[importpath] = exports
				pkgsMu.Unlock()
			}
		}(pkg.importpath, pkg.dir)
	}
	pkgIndex.Unlock()
	wg.Wait()

	// Filter out packages missing required exported symbols.
	for symbol := range symbols {
		for importpath, exports := range pkgs {
			if !exports[symbol] {
				delete(pkgs, importpath)
			}
		}
	}
	if len(pkgs) == 0 {
		return "", false, nil
	}

	// If there are multiple candidate packages, the shortest one wins.
	// This is a heuristic to prefer the standard library (e.g. "bytes")
	// over e.g. "github.com/foo/bar/bytes".
	shortest := ""
	for importPath := range pkgs {
		if shortest == "" || len(importPath) < len(shortest) {
			shortest = importPath
		}
	}
	return shortest, false, nil
}

type visitFn func(node ast.Node) ast.Visitor

func (fn visitFn) Visit(node ast.Node) ast.Visitor {
	return fn(node)
}

func findImportStdlib(shortPkg string, symbols map[string]bool) (importPath string, rename, ok bool) {
	for symbol := range symbols {
		path := stdlib[shortPkg+"."+symbol]
		if path == "" {
			return "", false, false
		}
		if importPath != "" && importPath != path {
			// Ambiguous. Symbols pointed to different things.
			return "", false, false
		}
		importPath = path
	}
	return importPath, false, importPath != ""
}
<commit_msg>imports: limit local disk concurrency, avoid reads in non-Go directories<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package imports

import (
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"

	"code.google.com/p/go.tools/astutil"
)

// importToGroup is a list of functions which map from an import path to
// a group number.
var importToGroup = []func(importPath string) (num int, ok bool){
	func(importPath string) (num int, ok bool) {
		if strings.HasPrefix(importPath, "appengine") {
			return 2, true
		}
		return
	},
	func(importPath string) (num int, ok bool) {
		if strings.Contains(importPath, ".") {
			return 1, true
		}
		return
	},
}

func importGroup(importPath string) int {
	for _, fn := range importToGroup {
		if n, ok := fn(importPath); ok {
			return n
		}
	}
	return 0
}

func fixImports(fset *token.FileSet, f *ast.File) (added []string, err error) {
	// refs are a set of possible package references currently unsatisified by imports.
	// first key: either base package (e.g. "fmt") or renamed package
	// second key: referenced package symbol (e.g. "Println")
	refs := make(map[string]map[string]bool)

	// decls are the current package imports. key is base package or renamed package.
	decls := make(map[string]*ast.ImportSpec)

	// collect potential uses of packages.
	var visitor visitFn
	visitor = visitFn(func(node ast.Node) ast.Visitor {
		if node == nil {
			return visitor
		}
		switch v := node.(type) {
		case *ast.ImportSpec:
			if v.Name != nil {
				decls[v.Name.Name] = v
			} else {
				local := importPathToName(strings.Trim(v.Path.Value, `\"`))
				decls[local] = v
			}
		case *ast.SelectorExpr:
			xident, ok := v.X.(*ast.Ident)
			if !ok {
				break
			}
			if xident.Obj != nil {
				// if the parser can resolve it, it's not a package ref
				break
			}
			pkgName := xident.Name
			if refs[pkgName] == nil {
				refs[pkgName] = make(map[string]bool)
			}
			if decls[pkgName] == nil {
				refs[pkgName][v.Sel.Name] = true
			}
		}
		return visitor
	})
	ast.Walk(visitor, f)

	// Search for imports matching potential package references.
	searches := 0
	type result struct {
		ipath string
		name  string
		err   error
	}
	results := make(chan result)
	for pkgName, symbols := range refs {
		if len(symbols) == 0 {
			continue // skip over packages already imported
		}
		go func(pkgName string, symbols map[string]bool) {
			ipath, rename, err := findImport(pkgName, symbols)
			r := result{ipath: ipath, err: err}
			if rename {
				r.name = pkgName
			}
			results <- r
		}(pkgName, symbols)
		searches++
	}
	for i := 0; i < searches; i++ {
		result := <-results
		if result.err != nil {
			return nil, result.err
		}
		if result.ipath != "" {
			if result.name != "" {
				astutil.AddNamedImport(fset, f, result.name, result.ipath)
			} else {
				astutil.AddImport(fset, f, result.ipath)
			}
			added = append(added, result.ipath)
		}
	}

	// Nil out any unused ImportSpecs, to be removed in following passes
	unusedImport := map[string]bool{}
	for pkg, is := range decls {
		if refs[pkg] == nil && pkg != "_" && pkg != "." {
			unusedImport[strings.Trim(is.Path.Value, `"`)] = true
		}
	}
	for ipath := range unusedImport {
		if ipath == "C" {
			// Don't remove cgo stuff.
			continue
		}
		astutil.DeleteImport(fset, f, ipath)
	}

	return added, nil
}

// importPathToName returns the package name for the given import path.
var importPathToName = importPathToNameGoPath

// importPathToNameBasic assumes the package name is the base of import path.
func importPathToNameBasic(importPath string) (packageName string) {
	return path.Base(importPath)
}

// importPathToNameGoPath finds out the actual package name, as declared in its .go files.
// If there's a problem, it falls back to using importPathToNameBasic.
func importPathToNameGoPath(importPath string) (packageName string) {
	if buildPkg, err := build.Import(importPath, "", 0); err == nil {
		return buildPkg.Name
	} else {
		return importPathToNameBasic(importPath)
	}
}

type pkg struct {
	importpath string // full pkg import path, e.g. "net/http"
	dir        string // absolute file path to pkg directory e.g. "/usr/lib/go/src/fmt"
}

var pkgIndexOnce sync.Once

var pkgIndex struct {
	sync.Mutex
	m map[string][]pkg // shortname => []pkg, e.g "http" => "net/http"
}

// gate is a semaphore for limiting concurrency.
type gate chan bool

func (g gate) enter() { g <- true }
func (g gate) leave() { <-g }

// fsgate protects the OS & filesystem from too much concurrency.
// Too much disk I/O -> too many threads -> swapping and bad scheduling.
var fsgate = make(gate, 8)

func loadPkgIndex() {
	pkgIndex.Lock()
	pkgIndex.m = make(map[string][]pkg)
	pkgIndex.Unlock()

	var wg sync.WaitGroup
	for _, path := range build.Default.SrcDirs() {
		fsgate.enter()
		f, err := os.Open(path)
		if err != nil {
			fsgate.leave()
			fmt.Fprint(os.Stderr, err)
			continue
		}
		children, err := f.Readdir(-1)
		f.Close()
		fsgate.leave()
		if err != nil {
			fmt.Fprint(os.Stderr, err)
			continue
		}
		for _, child := range children {
			if child.IsDir() {
				wg.Add(1)
				go func(path, name string) {
					defer wg.Done()
					loadPkg(&wg, path, name)
				}(path, child.Name())
			}
		}
	}
	wg.Wait()
}

func loadPkg(wg *sync.WaitGroup, root, pkgrelpath string) {
	importpath := filepath.ToSlash(pkgrelpath)
	dir := filepath.Join(root, importpath)

	fsgate.enter()
	defer fsgate.leave()
	pkgDir, err := os.Open(dir)
	if err != nil {
		return
	}
	children, err := pkgDir.Readdir(-1)
	pkgDir.Close()
	if err != nil {
		return
	}
	// hasGo tracks whether a directory actually appears to be a
	// Go source code directory. If $GOPATH == $HOME, and
	// $HOME/src has lots of other large non-Go projects in it,
	// then the calls to importPathToName below can be expensive.
	hasGo := false
	for _, child := range children {
		name := child.Name()
		if name == "" {
			continue
		}
		if c := name[0]; c == '.' || ('0' <= c && c <= '9') {
			continue
		}
		if strings.HasSuffix(name, ".go") {
			hasGo = true
		}
		if child.IsDir() {
			wg.Add(1)
			go func(root, name string) {
				defer wg.Done()
				loadPkg(wg, root, name)
			}(root, filepath.Join(importpath, name))
		}
	}
	if hasGo {
		shortName := importPathToName(importpath)
		pkgIndex.Lock()
		pkgIndex.m[shortName] = append(pkgIndex.m[shortName], pkg{
			importpath: importpath,
			dir:        dir,
		})
		pkgIndex.Unlock()
	}

}

// loadExports returns a list exports for a package.
var loadExports = loadExportsGoPath

func loadExportsGoPath(dir string) map[string]bool {
	exports := make(map[string]bool)
	buildPkg, err := build.ImportDir(dir, 0)
	if err != nil {
		if strings.Contains(err.Error(), "no buildable Go source files in") {
			return nil
		}
		fmt.Fprintf(os.Stderr, "could not import %q: %v", dir, err)
		return nil
	}
	fset := token.NewFileSet()
	for _, file := range buildPkg.GoFiles {
		f, err := parser.ParseFile(fset, filepath.Join(dir, file), nil, 0)
		if err != nil {
			fmt.Fprintf(os.Stderr, "could not parse %q: %v", file, err)
			continue
		}
		for name := range f.Scope.Objects {
			if ast.IsExported(name) {
				exports[name] = true
			}
		}
	}
	return exports
}

// findImport searches for a package with the given symbols.
// If no package is found, findImport returns "".
// Declared as a variable rather than a function so goimports can be easily
// extended by adding a file with an init function.
var findImport = findImportGoPath

func findImportGoPath(pkgName string, symbols map[string]bool) (string, bool, error) {
	// Fast path for the standard library.
	// In the common case we hopefully never have to scan the GOPATH, which can
	// be slow with moving disks.
	if pkg, rename, ok := findImportStdlib(pkgName, symbols); ok {
		return pkg, rename, nil
	}

	// TODO(sameer): look at the import lines for other Go files in the
	// local directory, since the user is likely to import the same packages
	// in the current Go file.  Return rename=true when the other Go files
	// use a renamed package that's also used in the current file.

	pkgIndexOnce.Do(loadPkgIndex)

	// Collect exports for packages with matching names.
	var wg sync.WaitGroup
	var pkgsMu sync.Mutex // guards pkgs
	// full importpath => exported symbol => True
	// e.g. "net/http" => "Client" => True
	pkgs := make(map[string]map[string]bool)
	pkgIndex.Lock()
	for _, pkg := range pkgIndex.m[pkgName] {
		wg.Add(1)
		go func(importpath, dir string) {
			defer wg.Done()
			exports := loadExports(dir)
			if exports != nil {
				pkgsMu.Lock()
				pkgs[importpath] = exports
				pkgsMu.Unlock()
			}
		}(pkg.importpath, pkg.dir)
	}
	pkgIndex.Unlock()
	wg.Wait()

	// Filter out packages missing required exported symbols.
	for symbol := range symbols {
		for importpath, exports := range pkgs {
			if !exports[symbol] {
				delete(pkgs, importpath)
			}
		}
	}
	if len(pkgs) == 0 {
		return "", false, nil
	}

	// If there are multiple candidate packages, the shortest one wins.
	// This is a heuristic to prefer the standard library (e.g. "bytes")
	// over e.g. "github.com/foo/bar/bytes".
	shortest := ""
	for importPath := range pkgs {
		if shortest == "" || len(importPath) < len(shortest) {
			shortest = importPath
		}
	}
	return shortest, false, nil
}

type visitFn func(node ast.Node) ast.Visitor

func (fn visitFn) Visit(node ast.Node) ast.Visitor {
	return fn(node)
}

func findImportStdlib(shortPkg string, symbols map[string]bool) (importPath string, rename, ok bool) {
	for symbol := range symbols {
		path := stdlib[shortPkg+"."+symbol]
		if path == "" {
			return "", false, false
		}
		if importPath != "" && importPath != path {
			// Ambiguous. Symbols pointed to different things.
			return "", false, false
		}
		importPath = path
	}
	return importPath, false, importPath != ""
}
<|endoftext|>


<commit_before>package ihex

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"io"
	"strings"
)

type Writer struct {
	w     io.Writer
	width int
	addr  uint16
	fifo  bytes.Buffer
}

func NewWriterWidth(w io.Writer, width int) *Writer {
	var ihw Writer
	ihw.width = width
	ihw.w = w
	return &ihw
}

func NewWriter(w io.Writer) *Writer {
	return NewWriterWidth(w, 16)
}

func (x *Writer) SetAddress(a uint16) {
	x.addr = a
}

func (x *Writer) emitDataRecord(p []byte) error {
	// collect all the stuff that goes into this type of record
	var data = []interface{}{
		byte(len(p)), // byte count
		x.addr,       // standard 16-bit base address
		byte(0),      // record type
		p,            // slice of data
	}

	err := x.emitRecord(data)
	if err != nil {
		return fmt.Errorf("emitDataRecord: %v", err)
	}

	x.addr += uint16(len(p))

	return nil
}

func (x *Writer) Write(p []byte) (int, error) {
	var (
		originalXferLen int = len(p)
		xferLen         int
	)

	// Write caller's data to our internal FIFO
	x.fifo.Write(p)

	// Only write hex records 'width' wide.  Residual will be
	// held in the FIFO until a follow-up write(), a Flush() or
	// Writer is closed.
	for x.fifo.Len() >= x.width {
		err := x.emitDataRecord(x.fifo.Next(x.width))
		if err != nil {
			return xferLen, err
		}
		xferLen += x.width
	}

	return originalXferLen, nil
}

func (x *Writer) Flush() error {
	err := x.emitDataRecord(x.fifo.Next(x.fifo.Len()))
	if err != nil {
		return err
	}
	return nil
}

// Note: the underlying io.Writer is NOT closed
func (x *Writer) Close() error {
	// collect all the stuff that goes into this type of record
	var data = []interface{}{
		byte(0),   // byte count
		uint16(0), // standard 16-bit base address
		byte(1),   // record type (EOF)
	}

	// Write the Intel Hex EOF record; this should be the last
	// entity written to the stream
	return x.emitRecord(data)
	// Note: the underlying writer is _NOT_ closed; that is left
	// to the upper level code.
}

func (x *Writer) emitRecord(data []interface{}) error {
	buf := new(bytes.Buffer)

	// construct segment address record image
	for _, v := range data {
		err := binary.Write(buf, binary.BigEndian, v)
		if err != nil {
			return fmt.Errorf("Failure converting hex record to binary: %v", err)
		}
	}

	// append checksum
	err := buf.WriteByte(calcChecksum(buf.Bytes()))
	if err != nil {
		return fmt.Errorf("internal inconsistency writing to bytes.Buffer: %v", err)
	}

	s := strings.ToUpper(hex.EncodeToString(buf.Bytes()))
	_, err = fmt.Fprintf(x.w, ":%s\n", s)
	if err != nil {
		return fmt.Errorf("Failure formatting Intel Hex record: %v", err)
	}

	return nil
}

func (x *Writer) WriteExSegAddr(sa uint16) error {
	// collect all the stuff that goes into this type of record
	var data = []interface{}{
		byte(2),   // byte count
		uint16(0), // standard 16-bit base address
		byte(2),   // record type
		sa,        // caller's segment address
	}

	return x.emitRecord(data)
}

func (x *Writer) WriteStartSegAddr(cs, ip uint16) error {
	// collect all the stuff that goes into this type of record
	var data = []interface{}{
		byte(4),   // byte count
		uint16(0), // standard 16-bit base address
		byte(3),   // record type
		cs,        // 80x86 processor code segment value
		ip,        // 80x86 processor IP register value
	}

	return x.emitRecord(data)
}

// Write Extended Linear Address record
func (x *Writer) WriteExtLinAddr(addr uint16) error {
	// collect all the stuff that goes into this type of record
	var data = []interface{}{
		byte(2),   // byte count
		uint16(0), // standard 16-bit base address
		byte(4),   // record type
		addr,      // upper 16-bits for all 00 type records
	}

	return x.emitRecord(data)
}

// Write Start Extended Linear Address record
func (x *Writer) WriteStartLinAddr(eip uint32) error {
	// collect all the stuff that goes into this type of record
	var data = []interface{}{
		byte(4),   // byte count
		uint16(0), // standard 16-bit base address
		byte(5),   // record type
		eip,       // 32-bit value loaded into the EIP register
	}

	return x.emitRecord(data)
}

// Calculate checksum value based on Intel Hex Spec
func calcChecksum(buf []byte) byte {
	var cs int
	for _, b := range buf {
		cs += int(b)
	}
	return byte(-cs)
}
<commit_msg>Close() and Flush() mods<commit_after>package ihex

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"io"
	"strings"
)

type Writer struct {
	w     io.Writer
	width int
	addr  uint16
	fifo  bytes.Buffer
}

func NewWriterWidth(w io.Writer, width int) *Writer {
	var ihw Writer
	ihw.width = width
	ihw.w = w
	return &ihw
}

func NewWriter(w io.Writer) *Writer {
	return NewWriterWidth(w, 16)
}

func (x *Writer) SetAddress(a uint16) {
	x.addr = a
}

func (x *Writer) emitDataRecord(p []byte) error {
	// collect all the stuff that goes into this type of record
	var data = []interface{}{
		byte(len(p)), // byte count
		x.addr,       // standard 16-bit base address
		byte(0),      // record type
		p,            // slice of data
	}

	err := x.emitRecord(data)
	if err != nil {
		return fmt.Errorf("emitDataRecord: %v", err)
	}

	x.addr += uint16(len(p))

	return nil
}

func (x *Writer) Write(p []byte) (int, error) {
	var (
		originalXferLen int = len(p)
		xferLen         int
	)

	// Write caller's data to our internal FIFO
	x.fifo.Write(p)

	// Only write hex records 'width' wide.  Residual will be
	// held in the FIFO until a follow-up write(), a Flush() or
	// Writer is closed.
	for x.fifo.Len() >= x.width {
		err := x.emitDataRecord(x.fifo.Next(x.width))
		if err != nil {
			return xferLen, err
		}
		xferLen += x.width
	}

	return originalXferLen, nil
}

func (x *Writer) Flush() error {
	if x.fifo.Len() > 0 {
		err := x.emitDataRecord(x.fifo.Next(x.fifo.Len()))
		if err != nil {
			return err
		}
	}
	return nil
}

// Note: the underlying io.Writer is NOT closed
func (x *Writer) Close() error {
	x.Flush()

	// collect all the stuff that goes into this type of record
	var data = []interface{}{
		byte(0),   // byte count
		uint16(0), // standard 16-bit base address
		byte(1),   // record type (EOF)
	}

	// Write the Intel Hex EOF record; this should be the last
	// entity written to the stream
	return x.emitRecord(data)
	// Note: the underlying writer is _NOT_ closed; that is left
	// to the upper level code.
}

func (x *Writer) emitRecord(data []interface{}) error {
	buf := new(bytes.Buffer)

	// construct segment address record image
	for _, v := range data {
		err := binary.Write(buf, binary.BigEndian, v)
		if err != nil {
			return fmt.Errorf("Failure converting hex record to binary: %v", err)
		}
	}

	// append checksum
	err := buf.WriteByte(calcChecksum(buf.Bytes()))
	if err != nil {
		return fmt.Errorf("internal inconsistency writing to bytes.Buffer: %v", err)
	}

	s := strings.ToUpper(hex.EncodeToString(buf.Bytes()))
	_, err = fmt.Fprintf(x.w, ":%s\n", s)
	if err != nil {
		return fmt.Errorf("Failure formatting Intel Hex record: %v", err)
	}

	return nil
}

func (x *Writer) WriteExSegAddr(sa uint16) error {
	// collect all the stuff that goes into this type of record
	var data = []interface{}{
		byte(2),   // byte count
		uint16(0), // standard 16-bit base address
		byte(2),   // record type
		sa,        // caller's segment address
	}

	return x.emitRecord(data)
}

func (x *Writer) WriteStartSegAddr(cs, ip uint16) error {
	// collect all the stuff that goes into this type of record
	var data = []interface{}{
		byte(4),   // byte count
		uint16(0), // standard 16-bit base address
		byte(3),   // record type
		cs,        // 80x86 processor code segment value
		ip,        // 80x86 processor IP register value
	}

	return x.emitRecord(data)
}

// Write Extended Linear Address record
func (x *Writer) WriteExtLinAddr(addr uint16) error {
	// collect all the stuff that goes into this type of record
	var data = []interface{}{
		byte(2),   // byte count
		uint16(0), // standard 16-bit base address
		byte(4),   // record type
		addr,      // upper 16-bits for all 00 type records
	}

	return x.emitRecord(data)
}

// Write Start Extended Linear Address record
func (x *Writer) WriteStartLinAddr(eip uint32) error {
	// collect all the stuff that goes into this type of record
	var data = []interface{}{
		byte(4),   // byte count
		uint16(0), // standard 16-bit base address
		byte(5),   // record type
		eip,       // 32-bit value loaded into the EIP register
	}

	return x.emitRecord(data)
}

// Calculate checksum value based on Intel Hex Spec
func calcChecksum(buf []byte) byte {
	var cs int
	for _, b := range buf {
		cs += int(b)
	}
	return byte(-cs)
}
<|endoftext|>