<commit_before>package main

import (
	"bufio"
	"fmt"
	"github.com/santiaago/caltechx.go/linreg"
	"log"
	"math"
	"os"
	"runtime"
	"strconv"
	"strings"
	"time"
)

// measure will measure the time taken by function f to run and display it.
func measure(f func(), name string) {
	start := time.Now()
	f()
	elapsed := time.Since(start)
	fmt.Printf("%s took %4.2f seconds\n", name, elapsed.Seconds())
}

func getData(filename string) [][]float64 {

	file, err := os.Open(filename)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	var data [][]float64
	scanner := bufio.NewScanner(file)
	numberOfLines := 0
	for scanner.Scan() {

		split := strings.Split(scanner.Text(), " ")
		var line []string
		for _, s := range split {
			cell := strings.Replace(s, " ", "", -1)
			if len(cell) > 0 {
				line = append(line, cell)
			}
		}

		sample := make([]float64, 0)

		if x1, err := strconv.ParseFloat(line[0], 64); err != nil {
			fmt.Printf("x1 unable to parse line %d in file %s\n", numberOfLines, filename)
		} else {
			sample = append(sample, x1)
		}
		if x2, err := strconv.ParseFloat(line[1], 64); err != nil {
			fmt.Printf("x2 unable to parse line %d in file %s\n", numberOfLines, filename)
		} else {
			sample = append(sample, x2)
		}
		if y, err := strconv.ParseFloat(line[2], 64); err != nil {
			fmt.Printf("y unable to parse line %d in file %s\n", numberOfLines, filename)
		} else {
			sample = append(sample, y)
		}
		data = append(data, sample)
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)

	}
	return data
}

type nonLinearTransformFunc func(x []float64) []float64

// non linear transformations

func phi0(x []float64) []float64 {
	return []float64{float64(x[0])}
}
func phi1(x []float64) []float64 {
	return []float64{float64(x[0]), float64(x[1])}
}
func phi2(x []float64) []float64 {
	return []float64{float64(x[0]), float64(x[1]), float64(x[2])}
}
func phi3(x []float64) []float64 {
	return []float64{float64(x[0]), float64(x[1]), float64(x[2]), x[1] * x[1]}
}
func phi4(x []float64) []float64 {
	return []float64{float64(x[0]), float64(x[1]), float64(x[2]), x[1] * x[1], x[2] * x[2]}
}
func phi5(x []float64) []float64 {
	return []float64{float64(x[0]), float64(x[1]), float64(x[2]), x[1] * x[1], x[2] * x[2], x[1] * x[2]}
}
func phi6(x []float64) []float64 {
	return []float64{float64(x[0]), float64(x[1]), float64(x[2]), x[1] * x[1], x[2] * x[2], x[1] * x[2], math.Abs(x[1] - x[2])}
}
func phi7(x []float64) []float64 {
	return []float64{float64(x[0]), float64(x[1]), float64(x[2]), x[1] * x[1], x[2] * x[2], x[1] * x[2], math.Abs(x[1] - x[2]), math.Abs(x[1] + x[2])}
}

func q1() {
	fns := []linreg.TransformFunc{phi0, phi1, phi2, phi3, phi4, phi5, phi6, phi7}

	ks := []int{3, 4, 5, 6, 7}

	data := getData("data/in.dta")
	for _, k := range ks {
		linreg := linreg.NewLinearRegression()

		linreg.InitializeFromData(data[:25])
		linreg.InitializeValidationFromData(data[25:])

		linreg.TransformFunction = fns[k]

		linreg.ApplyTransformation()
		linreg.ApplyTransformationOnValidation()
		linreg.Learn()
		eIn := linreg.Ein()
		eVal := linreg.EValIn()
		eOut, _ := linreg.EoutFromFile("data/out.dta")

		fmt.Printf("EVal = %f, for k = %d\n", eVal, k)
		fmt.Printf("EIn = %f, for k = %d\n", eIn, k)
		fmt.Printf("EOut = %f, for k = %d\n", eOut, k)
		fmt.Println()
	}
}

func q3() {
	fns := []linreg.TransformFunc{phi0, phi1, phi2, phi3, phi4, phi5, phi6, phi7}

	ks := []int{3, 4, 5, 6, 7}

	data := getData("data/in.dta")
	for _, k := range ks {
		linreg := linreg.NewLinearRegression()

		linreg.InitializeFromData(data[25:])
		linreg.InitializeValidationFromData(data[:25])

		linreg.TransformFunction = fns[k]

		linreg.ApplyTransformation()
		linreg.ApplyTransformationOnValidation()
		linreg.Learn()
		eIn := linreg.Ein()
		eVal := linreg.EValIn()
		eOut, _ := linreg.EoutFromFile("data/out.dta")

		fmt.Printf("EVal = %f, for k = %d\n", eVal, k)
		fmt.Printf("EIn = %f, for k = %d\n", eIn, k)
		fmt.Printf("EOut = %f, for k = %d\n", eOut, k)
		fmt.Println()
	}
}

func main() {
	fmt.Println("Num CPU: ", runtime.NumCPU())
	runtime.GOMAXPROCS(runtime.NumCPU())
	fmt.Println("week 7")
	fmt.Println("1")
	measure(q1, "q1")
	fmt.Println("2")
	fmt.Println("3")
	measure(q3, "q3")
	fmt.Println("4")
	fmt.Println("5")
	fmt.Println("6")
	fmt.Println("7")
	fmt.Println("8")
	fmt.Println("9")
	fmt.Println("10")
}
<commit_msg>hw7 q6<commit_after>package main

import (
	"bufio"
	"fmt"
	"github.com/santiaago/caltechx.go/linear"
	"github.com/santiaago/caltechx.go/linreg"
	"log"
	"math"
	"os"
	"runtime"
	"strconv"
	"strings"
	"time"
)

// measure will measure the time taken by function f to run and display it.
func measure(f func(), name string) {
	start := time.Now()
	f()
	elapsed := time.Since(start)
	fmt.Printf("%s took %4.2f seconds\n", name, elapsed.Seconds())
}

func getData(filename string) [][]float64 {

	file, err := os.Open(filename)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	var data [][]float64
	scanner := bufio.NewScanner(file)
	numberOfLines := 0
	for scanner.Scan() {

		split := strings.Split(scanner.Text(), " ")
		var line []string
		for _, s := range split {
			cell := strings.Replace(s, " ", "", -1)
			if len(cell) > 0 {
				line = append(line, cell)
			}
		}

		sample := make([]float64, 0)

		if x1, err := strconv.ParseFloat(line[0], 64); err != nil {
			fmt.Printf("x1 unable to parse line %d in file %s\n", numberOfLines, filename)
		} else {
			sample = append(sample, x1)
		}
		if x2, err := strconv.ParseFloat(line[1], 64); err != nil {
			fmt.Printf("x2 unable to parse line %d in file %s\n", numberOfLines, filename)
		} else {
			sample = append(sample, x2)
		}
		if y, err := strconv.ParseFloat(line[2], 64); err != nil {
			fmt.Printf("y unable to parse line %d in file %s\n", numberOfLines, filename)
		} else {
			sample = append(sample, y)
		}
		data = append(data, sample)
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)

	}
	return data
}

type nonLinearTransformFunc func(x []float64) []float64

// non linear transformations

func phi0(x []float64) []float64 {
	return []float64{float64(x[0])}
}
func phi1(x []float64) []float64 {
	return []float64{float64(x[0]), float64(x[1])}
}
func phi2(x []float64) []float64 {
	return []float64{float64(x[0]), float64(x[1]), float64(x[2])}
}
func phi3(x []float64) []float64 {
	return []float64{float64(x[0]), float64(x[1]), float64(x[2]), x[1] * x[1]}
}
func phi4(x []float64) []float64 {
	return []float64{float64(x[0]), float64(x[1]), float64(x[2]), x[1] * x[1], x[2] * x[2]}
}
func phi5(x []float64) []float64 {
	return []float64{float64(x[0]), float64(x[1]), float64(x[2]), x[1] * x[1], x[2] * x[2], x[1] * x[2]}
}
func phi6(x []float64) []float64 {
	return []float64{float64(x[0]), float64(x[1]), float64(x[2]), x[1] * x[1], x[2] * x[2], x[1] * x[2], math.Abs(x[1] - x[2])}
}
func phi7(x []float64) []float64 {
	return []float64{float64(x[0]), float64(x[1]), float64(x[2]), x[1] * x[1], x[2] * x[2], x[1] * x[2], math.Abs(x[1] - x[2]), math.Abs(x[1] + x[2])}
}

func q1() {
	fns := []linreg.TransformFunc{phi0, phi1, phi2, phi3, phi4, phi5, phi6, phi7}

	ks := []int{3, 4, 5, 6, 7}

	data := getData("data/in.dta")
	for _, k := range ks {
		linreg := linreg.NewLinearRegression()

		linreg.InitializeFromData(data[:25])
		linreg.InitializeValidationFromData(data[25:])

		linreg.TransformFunction = fns[k]

		linreg.ApplyTransformation()
		linreg.ApplyTransformationOnValidation()
		linreg.Learn()
		eIn := linreg.Ein()
		eVal := linreg.EValIn()
		eOut, _ := linreg.EoutFromFile("data/out.dta")

		fmt.Printf("EVal = %f, for k = %d\n", eVal, k)
		fmt.Printf("EIn = %f, for k = %d\n", eIn, k)
		fmt.Printf("EOut = %f, for k = %d\n", eOut, k)
		fmt.Println()
	}
}

func q3() {
	fns := []linreg.TransformFunc{phi0, phi1, phi2, phi3, phi4, phi5, phi6, phi7}

	ks := []int{3, 4, 5, 6, 7}

	data := getData("data/in.dta")
	for _, k := range ks {
		linreg := linreg.NewLinearRegression()

		linreg.InitializeFromData(data[25:])
		linreg.InitializeValidationFromData(data[:25])

		linreg.TransformFunction = fns[k]

		linreg.ApplyTransformation()
		linreg.ApplyTransformationOnValidation()
		linreg.Learn()
		eIn := linreg.Ein()
		eVal := linreg.EValIn()
		eOut, _ := linreg.EoutFromFile("data/out.dta")

		fmt.Printf("EVal = %f, for k = %d\n", eVal, k)
		fmt.Printf("EIn = %f, for k = %d\n", eIn, k)
		fmt.Printf("EOut = %f, for k = %d\n", eOut, k)
		fmt.Println()
	}
}

func min(a, b float64) float64 {
	if a < b {
		return a
	}
	return b
}

func q6() {
	runs := float64(10000)
	interval1 := linear.Interval{0, 1}
	interval2 := linear.Interval{0, 1}

	sumE1 := float64(0)
	sumE2 := float64(0)
	sumE := float64(0)
	for i := 0; i < int(runs); i++ {
		e1 := interval1.RandFloat()
		e2 := interval2.RandFloat()
		sumE1 += e1
		sumE2 += e2
		sumE += min(e1, e2)
	}
	fmt.Printf("e1 = %f, e2 = %f, e = %f\n", sumE1/runs, sumE2/runs, sumE/runs)
}
func main() {
	fmt.Println("Num CPU: ", runtime.NumCPU())
	runtime.GOMAXPROCS(runtime.NumCPU())
	fmt.Println("week 7")
	fmt.Println("1")
	measure(q1, "q1")
	fmt.Println("2")
	fmt.Println("3")
	measure(q3, "q3")
	fmt.Println("4")
	fmt.Println("5")
	fmt.Println("6")
	measure(q6, "q6")
	fmt.Println("7")
	fmt.Println("8")
	fmt.Println("9")
	fmt.Println("10")
}
<|endoftext|>


<commit_before>package coal

import (
	"context"

	"github.com/256dpi/lungo"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"

	"github.com/256dpi/fire/cinder"
)

// Collection wraps a collection to automatically push tracing spans for
// run queries.
type Collection struct {
	coll  lungo.ICollection
	trace *cinder.Trace
}

// AggregateAll wraps the native Aggregate collection method and decodes all
// documents to the provided slice.
func (c *Collection) AggregateAll(ctx context.Context, slicePtr interface{}, pipeline interface{}, opts ...*options.AggregateOptions) error {
	// push span
	c.trace.Push("coal/Collection.Aggregate")
	c.trace.Tag("pipeline", pipeline)
	defer c.trace.Pop()

	// run query
	csr, err := c.coll.Aggregate(ctx, pipeline, opts...)
	if err != nil {
		return err
	}

	// decode all documents
	err = csr.All(ctx, slicePtr)
	if err != nil {
		return err
	}

	return nil
}

// AggregateIter wraps the native Aggregate collection method and calls the
// provided callback with the decode method until an error is returned or the
// cursor has been exhausted.
func (c *Collection) AggregateIter(ctx context.Context, pipeline interface{}, fn func(func(interface{}) error) error, opts ...*options.AggregateOptions) error {
	// push span
	c.trace.Push("coal/Collection.Aggregate")
	c.trace.Tag("pipeline", pipeline)
	defer c.trace.Pop()

	// run query
	csr, err := c.coll.Aggregate(ctx, pipeline, opts...)
	if err != nil {
		return err
	}

	// ensure cursor is closed
	defer csr.Close(ctx)

	// iterate over all documents
	for csr.Next(ctx) {
		err = fn(csr.Decode)
		if err != nil {
			return err
		}
	}

	// close cursor
	err = csr.Close(nil)
	if err != nil {
		return err
	}

	return nil
}

// BulkWrite wraps the native BulkWrite collection method.
func (c *Collection) BulkWrite(ctx context.Context, models []mongo.WriteModel, opts ...*options.BulkWriteOptions) (*mongo.BulkWriteResult, error) {
	// push span
	c.trace.Push("coal/Collection.BulkWrite")
	defer c.trace.Pop()

	// run query
	return c.coll.BulkWrite(ctx, models, opts...)
}

// CountDocuments wraps the native CountDocuments collection method.
func (c *Collection) CountDocuments(ctx context.Context, filter interface{}, opts ...*options.CountOptions) (int64, error) {
	// push span
	c.trace.Push("coal/Collection.CountDocuments")
	c.trace.Log("filter", filter)
	defer c.trace.Pop()

	// run query
	return c.coll.CountDocuments(ctx, filter, opts...)
}

// DeleteMany wraps the native DeleteMany collection method.
func (c *Collection) DeleteMany(ctx context.Context, filter interface{}, opts ...*options.DeleteOptions) (*mongo.DeleteResult, error) {
	// push span
	c.trace.Push("coal/Collection.DeleteMany")
	c.trace.Log("filter", filter)
	defer c.trace.Pop()

	// run query
	return c.coll.DeleteMany(ctx, filter, opts...)
}

// DeleteOne wraps the native DeleteOne collection method.
func (c *Collection) DeleteOne(ctx context.Context, filter interface{}, opts ...*options.DeleteOptions) (*mongo.DeleteResult, error) {
	// push span
	c.trace.Push("coal/Collection.DeleteOne")
	c.trace.Log("filter", filter)
	defer c.trace.Pop()

	// run query
	return c.coll.DeleteOne(ctx, filter, opts...)
}

// Distinct wraps the native Distinct collection method.
func (c *Collection) Distinct(ctx context.Context, fieldName string, filter interface{}, opts ...*options.DistinctOptions) ([]interface{}, error) {
	// push span
	c.trace.Push("coal/Collection.Distinct")
	c.trace.Tag("fieldName", fieldName)
	c.trace.Log("filter", filter)
	defer c.trace.Pop()

	// run query
	return c.coll.Distinct(ctx, fieldName, filter, opts...)
}

// EstimatedDocumentCount wraps the native EstimatedDocumentCount collection method.
func (c *Collection) EstimatedDocumentCount(ctx context.Context, opts ...*options.EstimatedDocumentCountOptions) (int64, error) {
	// push span
	c.trace.Push("coal/Collection.EstimatedDocumentCount")
	defer c.trace.Pop()

	// run query
	return c.coll.EstimatedDocumentCount(ctx, opts...)
}

// FindAll wraps the native Find collection method and decodes all documents to
// the provided slice.
func (c *Collection) FindAll(ctx context.Context, slicePtr interface{}, filter interface{}, opts ...*options.FindOptions) error {
	// push span
	c.trace.Push("coal/Collection.Find")
	c.trace.Tag("filter", filter)
	defer c.trace.Pop()

	// run query
	csr, err := c.coll.Find(ctx, filter, opts...)
	if err != nil {
		return err
	}

	// decode all documents
	err = csr.All(ctx, slicePtr)
	if err != nil {
		return err
	}

	return nil
}

// FindIter wraps the native Find collection method and calls the provided
// callback with the decode method until an error is returned or the cursor has
// been exhausted.
func (c *Collection) FindIter(ctx context.Context, filter interface{}, fn func(func(interface{}) error) error, opts ...*options.FindOptions) error {
	// push span
	c.trace.Push("coal/Collection.Find")
	c.trace.Tag("filter", filter)
	defer c.trace.Pop()

	// run query
	csr, err := c.coll.Find(ctx, filter, opts...)
	if err != nil {
		return err
	}

	// ensure cursor is closed
	defer csr.Close(ctx)

	// iterate over all documents
	for csr.Next(ctx) {
		err = fn(csr.Decode)
		if err != nil {
			return err
		}
	}

	// close cursor
	err = csr.Close(nil)
	if err != nil {
		return err
	}

	return nil
}

// FindOne wraps the native FindOne collection method.
func (c *Collection) FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) lungo.ISingleResult {
	// push span
	c.trace.Push("coal/Collection.FindOne")
	c.trace.Log("filter", filter)
	defer c.trace.Pop()

	// run query
	return c.coll.FindOne(ctx, filter, opts...)
}

// FindOneAndDelete wraps the native FindOneAndDelete collection method.
func (c *Collection) FindOneAndDelete(ctx context.Context, filter interface{}, opts ...*options.FindOneAndDeleteOptions) lungo.ISingleResult {
	// push span
	c.trace.Push("coal/Collection.FindOneAndDelete")
	c.trace.Log("filter", filter)
	defer c.trace.Pop()

	// run query
	return c.coll.FindOneAndDelete(ctx, filter, opts...)
}

// FindOneAndReplace wraps the native FindOneAndReplace collection method.
func (c *Collection) FindOneAndReplace(ctx context.Context, filter interface{}, replacement interface{}, opts ...*options.FindOneAndReplaceOptions) lungo.ISingleResult {
	// push span
	c.trace.Push("coal/Collection.FindOneAndReplace")
	c.trace.Log("filter", filter)
	defer c.trace.Pop()

	// run query
	return c.coll.FindOneAndReplace(ctx, filter, replacement, opts...)
}

// FindOneAndUpdate wraps the native FindOneAndUpdate collection method.
func (c *Collection) FindOneAndUpdate(ctx context.Context, filter interface{}, update interface{}, opts ...*options.FindOneAndUpdateOptions) lungo.ISingleResult {
	// push span
	c.trace.Push("coal/Collection.FindOneAndUpdate")
	c.trace.Log("filter", filter)
	defer c.trace.Pop()

	// run query
	return c.coll.FindOneAndUpdate(ctx, filter, update, opts...)
}

// InsertMany wraps the native InsertMany collection method.
func (c *Collection) InsertMany(ctx context.Context, documents []interface{}, opts ...*options.InsertManyOptions) (*mongo.InsertManyResult, error) {
	// push span
	c.trace.Push("coal/Collection.InsertMany")
	defer c.trace.Pop()

	// run query
	return c.coll.InsertMany(ctx, documents, opts...)
}

// InsertOne wraps the native InsertOne collection method.
func (c *Collection) InsertOne(ctx context.Context, document interface{}, opts ...*options.InsertOneOptions) (*mongo.InsertOneResult, error) {
	// push span
	c.trace.Push("coal/Collection.InsertOne")
	defer c.trace.Pop()

	// run query
	return c.coll.InsertOne(ctx, document, opts...)
}

// ReplaceOne wraps the native ReplaceOne collection method.
func (c *Collection) ReplaceOne(ctx context.Context, filter interface{}, replacement interface{}, opts ...*options.ReplaceOptions) (*mongo.UpdateResult, error) {
	// push span
	c.trace.Push("coal/Collection.ReplaceOne")
	c.trace.Log("filter", filter)
	defer c.trace.Pop()

	// run query
	return c.coll.ReplaceOne(ctx, filter, replacement, opts...)
}

// UpdateMany wraps the native UpdateMany collection method.
func (c *Collection) UpdateMany(ctx context.Context, filter interface{}, update interface{}, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) {
	// push span
	c.trace.Push("coal/Collection.UpdateMany")
	c.trace.Log("filter", filter)
	defer c.trace.Pop()

	// run query
	return c.coll.UpdateMany(ctx, filter, update, opts...)
}

// UpdateOne wraps the native UpdateOne collection method.
func (c *Collection) UpdateOne(ctx context.Context, filter interface{}, update interface{}, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) {
	// push span
	c.trace.Push("coal/Collection.UpdateOne")
	c.trace.Log("filter", filter)
	defer c.trace.Pop()

	// run query
	return c.coll.UpdateOne(ctx, filter, update, opts...)
}
<commit_msg>make tracing optional<commit_after>package coal

import (
	"context"

	"github.com/256dpi/lungo"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"

	"github.com/256dpi/fire/cinder"
)

// Collection wraps a collection to automatically push tracing spans for
// run queries.
type Collection struct {
	coll  lungo.ICollection
	trace *cinder.Trace
}

// AggregateAll wraps the native Aggregate collection method and decodes all
// documents to the provided slice.
func (c *Collection) AggregateAll(ctx context.Context, slicePtr interface{}, pipeline interface{}, opts ...*options.AggregateOptions) error {
	// trace
	if c.trace != nil {
		c.trace.Push("coal/Collection.Aggregate")
		c.trace.Tag("pipeline", pipeline)
		defer c.trace.Pop()
	}

	// run query
	csr, err := c.coll.Aggregate(ctx, pipeline, opts...)
	if err != nil {
		return err
	}

	// decode all documents
	err = csr.All(ctx, slicePtr)
	if err != nil {
		return err
	}

	return nil
}

// AggregateIter wraps the native Aggregate collection method and calls the
// provided callback with the decode method until an error is returned or the
// cursor has been exhausted.
func (c *Collection) AggregateIter(ctx context.Context, pipeline interface{}, fn func(func(interface{}) error) error, opts ...*options.AggregateOptions) error {
	// trace
	if c.trace != nil {
		c.trace.Push("coal/Collection.Aggregate")
		c.trace.Tag("pipeline", pipeline)
		defer c.trace.Pop()
	}

	// run query
	csr, err := c.coll.Aggregate(ctx, pipeline, opts...)
	if err != nil {
		return err
	}

	// ensure cursor is closed
	defer csr.Close(ctx)

	// iterate over all documents
	for csr.Next(ctx) {
		err = fn(csr.Decode)
		if err != nil {
			return err
		}
	}

	// close cursor
	err = csr.Close(nil)
	if err != nil {
		return err
	}

	return nil
}

// BulkWrite wraps the native BulkWrite collection method.
func (c *Collection) BulkWrite(ctx context.Context, models []mongo.WriteModel, opts ...*options.BulkWriteOptions) (*mongo.BulkWriteResult, error) {
	// trace
	if c.trace != nil {
		// push span
		c.trace.Push("coal/Collection.BulkWrite")
		defer c.trace.Pop()
	}

	// run query
	return c.coll.BulkWrite(ctx, models, opts...)
}

// CountDocuments wraps the native CountDocuments collection method.
func (c *Collection) CountDocuments(ctx context.Context, filter interface{}, opts ...*options.CountOptions) (int64, error) {
	// trace
	if c.trace != nil {
		c.trace.Push("coal/Collection.CountDocuments")
		c.trace.Log("filter", filter)
		defer c.trace.Pop()
	}

	// run query
	return c.coll.CountDocuments(ctx, filter, opts...)
}

// DeleteMany wraps the native DeleteMany collection method.
func (c *Collection) DeleteMany(ctx context.Context, filter interface{}, opts ...*options.DeleteOptions) (*mongo.DeleteResult, error) {
	// trace
	if c.trace != nil {
		// push span
		c.trace.Push("coal/Collection.DeleteMany")
		c.trace.Log("filter", filter)
		defer c.trace.Pop()
	}

	// run query
	return c.coll.DeleteMany(ctx, filter, opts...)
}

// DeleteOne wraps the native DeleteOne collection method.
func (c *Collection) DeleteOne(ctx context.Context, filter interface{}, opts ...*options.DeleteOptions) (*mongo.DeleteResult, error) {
	// trace
	if c.trace != nil {
		// push span
		c.trace.Push("coal/Collection.DeleteOne")
		c.trace.Log("filter", filter)
		defer c.trace.Pop()
	}

	// run query
	return c.coll.DeleteOne(ctx, filter, opts...)
}

// Distinct wraps the native Distinct collection method.
func (c *Collection) Distinct(ctx context.Context, fieldName string, filter interface{}, opts ...*options.DistinctOptions) ([]interface{}, error) {
	// trace
	if c.trace != nil {
		// push span
		c.trace.Push("coal/Collection.Distinct")
		c.trace.Tag("fieldName", fieldName)
		c.trace.Log("filter", filter)
		defer c.trace.Pop()
	}

	// run query
	return c.coll.Distinct(ctx, fieldName, filter, opts...)
}

// EstimatedDocumentCount wraps the native EstimatedDocumentCount collection method.
func (c *Collection) EstimatedDocumentCount(ctx context.Context, opts ...*options.EstimatedDocumentCountOptions) (int64, error) {
	// trace
	if c.trace != nil {
		// push span
		c.trace.Push("coal/Collection.EstimatedDocumentCount")
		defer c.trace.Pop()
	}

	// run query
	return c.coll.EstimatedDocumentCount(ctx, opts...)
}

// FindAll wraps the native Find collection method and decodes all documents to
// the provided slice.
func (c *Collection) FindAll(ctx context.Context, slicePtr interface{}, filter interface{}, opts ...*options.FindOptions) error {
	// trace
	if c.trace != nil {
		// push span
		c.trace.Push("coal/Collection.Find")
		c.trace.Tag("filter", filter)
		defer c.trace.Pop()
	}

	// run query
	csr, err := c.coll.Find(ctx, filter, opts...)
	if err != nil {
		return err
	}

	// decode all documents
	err = csr.All(ctx, slicePtr)
	if err != nil {
		return err
	}

	return nil
}

// FindIter wraps the native Find collection method and calls the provided
// callback with the decode method until an error is returned or the cursor has
// been exhausted.
func (c *Collection) FindIter(ctx context.Context, filter interface{}, fn func(func(interface{}) error) error, opts ...*options.FindOptions) error {
	// trace
	if c.trace != nil {
		// push span
		c.trace.Push("coal/Collection.Find")
		c.trace.Tag("filter", filter)
		defer c.trace.Pop()
	}

	// run query
	csr, err := c.coll.Find(ctx, filter, opts...)
	if err != nil {
		return err
	}

	// ensure cursor is closed
	defer csr.Close(ctx)

	// iterate over all documents
	for csr.Next(ctx) {
		err = fn(csr.Decode)
		if err != nil {
			return err
		}
	}

	// close cursor
	err = csr.Close(nil)
	if err != nil {
		return err
	}

	return nil
}

// FindOne wraps the native FindOne collection method.
func (c *Collection) FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) lungo.ISingleResult {
	// trace
	if c.trace != nil {
		// push span
		c.trace.Push("coal/Collection.FindOne")
		c.trace.Log("filter", filter)
		defer c.trace.Pop()
	}

	// run query
	return c.coll.FindOne(ctx, filter, opts...)
}

// FindOneAndDelete wraps the native FindOneAndDelete collection method.
func (c *Collection) FindOneAndDelete(ctx context.Context, filter interface{}, opts ...*options.FindOneAndDeleteOptions) lungo.ISingleResult {
	// trace
	if c.trace != nil {
		// push span
		c.trace.Push("coal/Collection.FindOneAndDelete")
		c.trace.Log("filter", filter)
		defer c.trace.Pop()
	}

	// run query
	return c.coll.FindOneAndDelete(ctx, filter, opts...)
}

// FindOneAndReplace wraps the native FindOneAndReplace collection method.
func (c *Collection) FindOneAndReplace(ctx context.Context, filter interface{}, replacement interface{}, opts ...*options.FindOneAndReplaceOptions) lungo.ISingleResult {
	// trace
	if c.trace != nil {
		// push span
		c.trace.Push("coal/Collection.FindOneAndReplace")
		c.trace.Log("filter", filter)
		defer c.trace.Pop()
	}

	// run query
	return c.coll.FindOneAndReplace(ctx, filter, replacement, opts...)
}

// FindOneAndUpdate wraps the native FindOneAndUpdate collection method.
func (c *Collection) FindOneAndUpdate(ctx context.Context, filter interface{}, update interface{}, opts ...*options.FindOneAndUpdateOptions) lungo.ISingleResult {
	// trace
	if c.trace != nil {
		// push span
		c.trace.Push("coal/Collection.FindOneAndUpdate")
		c.trace.Log("filter", filter)
		defer c.trace.Pop()
	}

	// run query
	return c.coll.FindOneAndUpdate(ctx, filter, update, opts...)
}

// InsertMany wraps the native InsertMany collection method.
func (c *Collection) InsertMany(ctx context.Context, documents []interface{}, opts ...*options.InsertManyOptions) (*mongo.InsertManyResult, error) {
	// trace
	if c.trace != nil {
		// push span
		c.trace.Push("coal/Collection.InsertMany")
		defer c.trace.Pop()
	}

	// run query
	return c.coll.InsertMany(ctx, documents, opts...)
}

// InsertOne wraps the native InsertOne collection method.
func (c *Collection) InsertOne(ctx context.Context, document interface{}, opts ...*options.InsertOneOptions) (*mongo.InsertOneResult, error) {
	// trace
	if c.trace != nil {
		// push span
		c.trace.Push("coal/Collection.InsertOne")
		defer c.trace.Pop()
	}

	// run query
	return c.coll.InsertOne(ctx, document, opts...)
}

// ReplaceOne wraps the native ReplaceOne collection method.
func (c *Collection) ReplaceOne(ctx context.Context, filter interface{}, replacement interface{}, opts ...*options.ReplaceOptions) (*mongo.UpdateResult, error) {
	// trace
	if c.trace != nil {
		// push span
		c.trace.Push("coal/Collection.ReplaceOne")
		c.trace.Log("filter", filter)
		defer c.trace.Pop()
	}

	// run query
	return c.coll.ReplaceOne(ctx, filter, replacement, opts...)
}

// UpdateMany wraps the native UpdateMany collection method.
func (c *Collection) UpdateMany(ctx context.Context, filter interface{}, update interface{}, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) {
	// trace
	if c.trace != nil {
		// push span
		c.trace.Push("coal/Collection.UpdateMany")
		c.trace.Log("filter", filter)
		defer c.trace.Pop()
	}

	// run query
	return c.coll.UpdateMany(ctx, filter, update, opts...)
}

// UpdateOne wraps the native UpdateOne collection method.
func (c *Collection) UpdateOne(ctx context.Context, filter interface{}, update interface{}, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) {
	// trace
	if c.trace != nil {
		// push span
		c.trace.Push("coal/Collection.UpdateOne")
		c.trace.Log("filter", filter)
		defer c.trace.Pop()
	}

	// run query
	return c.coll.UpdateOne(ctx, filter, update, opts...)
}
<|endoftext|>


<commit_before>// Package game manages the flow and status of the game
package acquire

import (
	"errors"
	"github.com/svera/acquire/board"
	"github.com/svera/acquire/corporation"
	"github.com/svera/acquire/fsm"
	"github.com/svera/acquire/player"
	"github.com/svera/acquire/tile"
	"github.com/svera/acquire/tileset"
)

const (
	// ActionNotAllowed is an error returned when action not allowed at current state
	ActionNotAllowed = "action_not_allowed"
	// StockSharesNotBuyable is an error returned when stock shares from a corporation not on board are not buyable
	StockSharesNotBuyable = "stock_shares_not_buyable"
	// NotEnoughStockShares is an error returned when not enough stock shares of a corporation to buy
	NotEnoughStockShares = "not_enough_stock_shares"
	// TileTemporaryUnplayable is an error returned when tile temporarily unplayable
	TileTemporaryUnplayable = "tile_temporary_unplayable"
	// TilePermanentlyUnplayable is an error returned when tile permanently unplayable
	TilePermanentlyUnplayable = "tile_permanently_unplayable"
	// NotEnoughCash is an error returned when player has not enough cash to buy stock shares
	NotEnoughCash = "not_enough_cash"
	// TooManyStockSharesToBuy is an error returned when player can not buy more than 3 stock shares per turn
	TooManyStockSharesToBuy = "too_many_stock_shares_to_buy"
	// CorpNamesNotUnique is an error returned when some corporation names are repeated
	CorpNamesNotUnique = "corp_names_not_unique"
	// WrongNumberCorpsClass is an error returned when corporations classes do not fit rules
	WrongNumberCorpsClass = "wrong_number_corps_class"
	// CorporationAlreadyOnBoard is an error returned when corporation is already on board and cannot be founded
	CorporationAlreadyOnBoard = "corporation_already_on_board"
	// WrongNumberPlayers is an error returned when there must be between 3 and 6 players
	WrongNumberPlayers = "wrong_number_players"
	// NoCorporationSharesOwned is an error returned when player does not own stock shares of a certain corporation
	NoCorporationSharesOwned = "no_corporation_shares_owned"
	// NotEnoughCorporationSharesOwned is an error returned when player does not own enough stock shares of a certain corporation
	NotEnoughCorporationSharesOwned = "not_enough_corporation_shares_owned"
	// TileNotOnHand is an error returned when player does not have tile on hand
	TileNotOnHand = "tile_not_on_hand"
	// NotAnAcquirerCorporation is an error returned when corporation is not the acquirer in a merge
	NotAnAcquirerCorporation = "not_an_acquirer_corporation"
	// TradeAmountNotEven is an error returned when number of stock shares is not even in a trade
	TradeAmountNotEven = "trade_amount_not_even"

	totalCorporations      = 7
	endGameCorporationSize = 41
)

// Game stores state of game elements and provides methods to control game flow
type Game struct {
	board               board.Interface
	state               fsm.State
	players             []player.Interface
	corporations        [7]corporation.Interface
	tileset             tileset.Interface
	currentPlayerNumber int
	newCorpTiles        []tile.Interface
	mergeCorps          map[string][]corporation.Interface
	sellTradePlayers    []int
	lastPlayedTile      tile.Interface
	turn                int
	endGameClaimed      bool
	// When in sell_trade state, the current player is stored here temporary as the turn
	// is passed to all defunct corporations stockholders
	frozenPlayer int
}

// New initialises a new Acquire game
func New(
	board board.Interface, players []player.Interface, corporations [7]corporation.Interface, tileset tileset.Interface) (*Game, error) {
	if len(players) < 3 || len(players) > 6 {
		return nil, errors.New(WrongNumberPlayers)
	}
	if !areNamesUnique(corporations) {
		return nil, errors.New(CorpNamesNotUnique)
	}
	if !isNumberOfCorpsPerClassRight(corporations) {
		return nil, errors.New(WrongNumberCorpsClass)
	}
	gm := Game{
		board:               board,
		players:             players,
		corporations:        corporations,
		tileset:             tileset,
		currentPlayerNumber: 0,
		turn:                1,
		state:               &fsm.PlayTile{},
		endGameClaimed:      false,
	}
	for _, pl := range gm.players {
		gm.giveInitialTileset(pl)
	}

	return &gm, nil
}

// Check that the passed corporations have unique names
func areNamesUnique(corporations [7]corporation.Interface) bool {
	for i, corp1 := range corporations {
		if i < len(corporations)-1 {
			for _, corp2 := range corporations[i+1:] {
				if corp1.Name() == corp2.Name() {
					return false
				}
			}
		}
	}
	return true
}

// Check that the number of corporations per class is right
func isNumberOfCorpsPerClassRight(corporations [7]corporation.Interface) bool {
	corpsPerClass := [3]int{0, 0, 0}
	for _, corp := range corporations {
		corpsPerClass[corp.Class()]++
	}
	if corpsPerClass[0] != 2 || corpsPerClass[1] != 3 || corpsPerClass[2] != 2 {
		return false
	}
	return true
}

// Initialises player hand of tiles
func (g *Game) giveInitialTileset(plyr player.Interface) {
	for i := 0; i < 6; i++ {
		tile, _ := g.tileset.Draw()
		plyr.PickTile(tile)
	}
}

// AreEndConditionsReached check if game end conditions are reached
func (g *Game) AreEndConditionsReached() bool {
	active := g.getActiveCorporations()
	if len(active) == 0 {
		return false
	}
	for _, corp := range active {
		if corp.Size() >= endGameCorporationSize {
			return true
		}
		if !corp.IsSafe() {
			return false
		}
	}
	return true
}

// Returns all corporations on the board
func (g *Game) getActiveCorporations() []corporation.Interface {
	active := []corporation.Interface{}
	for _, corp := range g.corporations {
		if corp.IsActive() {
			active = append(active, corp)
		}
	}
	return active
}

// Returns true if a tile is permanently unplayable, that is,
// that putting it on the board would merge two or more safe corporations
func (g *Game) isTileUnplayable(tl tile.Interface) bool {
	adjacents := g.board.AdjacentCells(tl)
	safeNeighbours := 0
	for _, adjacent := range adjacents {
		if adjacent.Owner().Type() == "corporation" && adjacent.Owner().(corporation.Interface).IsSafe() {
			safeNeighbours++
		}
		if safeNeighbours == 2 {
			return true
		}
	}
	return false
}

// Returns true if a tile is temporarily unplayable, that is,
// that putting it on the board would create an 8th corporation
func (g *Game) isTileTemporaryUnplayable(tl tile.Interface) bool {
	if len(g.getActiveCorporations()) < totalCorporations {
		return false
	}
	adjacents := g.board.AdjacentCells(tl)
	for _, adjacent := range adjacents {
		if adjacent.Owner().Type() == "unincorporated" {
			return true
		}
	}
	return false
}

// Player returns player with passed number
func (g *Game) Player(playerNumber int) player.Interface {
	return g.players[playerNumber]
}

// CurrentPlayer returns player currently in play
func (g *Game) CurrentPlayer() player.Interface {
	return g.players[g.currentPlayerNumber]
}

// PlayTile puts the given tile on board and triggers related actions
func (g *Game) PlayTile(tl tile.Interface) error {
	if g.state.Name() != "PlayTile" {
		return errors.New(ActionNotAllowed)
	}
	if g.isTileTemporaryUnplayable(tl) {
		return errors.New(TileTemporaryUnplayable)
	}
	if !g.CurrentPlayer().HasTile(tl) {
		return errors.New(TileNotOnHand)
	}

	g.CurrentPlayer().DiscardTile(tl)
	g.lastPlayedTile = tl

	if merge, mergeCorps := g.board.TileMergeCorporations(tl); merge {
		g.mergeCorps = mergeCorps
		if g.isMergeTied() {
			g.state = g.state.ToUntieMerge()
		} else {
			for _, corp := range mergeCorps["defunct"] {
				g.payBonuses(corp)
			}
			g.sellTradePlayers = g.stockholders(mergeCorps["defunct"])
			g.frozenPlayer = g.currentPlayerNumber
			g.setCurrentPlayer(g.nextSellTradePlayer())
			g.state = g.state.ToSellTrade()
		}
	} else if found, tiles := g.board.TileFoundCorporation(tl); found {
		g.state = g.state.ToFoundCorp()
		g.newCorpTiles = tiles
	} else if grow, tiles, corp := g.board.TileGrowCorporation(tl); grow {
		g.growCorporation(corp, tiles)
		g.state = g.state.ToBuyStock()
	} else {
		g.board.PutTile(tl)
		g.state = g.state.ToBuyStock()
	}
	return nil
}

// Returns players who are shareholders of at least one of the passed companies
// starting from the current one in play (mergemaker)
func (g *Game) stockholders(corporations []corporation.Interface) []int {
	shareholders := []int{}
	index := g.currentPlayerNumber
	for _ = range g.players {
		for _, corp := range g.corporations {
			if g.players[index].Shares(corp) > 0 {
				shareholders = append(shareholders, index)
				break
			}
		}
		index++
		if index == len(g.players) {
			index = 0
		}
	}
	return shareholders
}

// Sets player currently in play
func (g *Game) setCurrentPlayer(number int) *Game {
	g.currentPlayerNumber = number
	return g
}

// FoundCorporation founds a new corporation
func (g *Game) FoundCorporation(corp corporation.Interface) error {
	if g.state.Name() != "FoundCorp" {
		return errors.New(ActionNotAllowed)
	}
	if corp.IsActive() {
		return errors.New(CorporationAlreadyOnBoard)
	}
	g.board.SetOwner(corp, g.newCorpTiles)
	corp.Grow(len(g.newCorpTiles))
	g.newCorpTiles = []tile.Interface{}
	g.getFounderStockShare(g.CurrentPlayer(), corp)
	g.state = g.state.ToBuyStock()
	return nil
}

// Receive a free stock share from a rencently found corporation, if it has
// remaining shares available
// TODO this should trigger an event warning that no founder stock share will be given
// of the founded corporation has no stock shares left
func (g *Game) getFounderStockShare(pl player.Interface, corp corporation.Interface) {
	if corp.Stock() > 0 {
		corp.RemoveStock(1)
		pl.AddShares(corp, 1)
	}
}

// Makes a corporation grow with the passed tiles
func (g *Game) growCorporation(corp corporation.Interface, tiles []tile.Interface) {
	g.board.SetOwner(corp, tiles)
	corp.Grow(len(tiles))
}

// Increases the number which specifies the current player
func (g *Game) nextPlayer() {
	g.currentPlayerNumber++
	if g.currentPlayerNumber == len(g.players) {
		g.currentPlayerNumber = 0
		g.turn++
	}
}

// Turn returns the current turn number
func (g *Game) Turn() int {
	return g.turn
}

// claimEndGame allows the current player to claim end game
// This can be done at any time. After announcing that the game is over,
// the player may finish the turn.
func (g *Game) ClaimEndGame() *Game {
	if g.AreEndConditionsReached() {
		g.endGameClaimed = true
	}
	return g
}

// Classification returns the players list ordered by cash,
// which is the metric used to know game's final classification
func (g *Game) Classification() []player.Interface {
	var classification []player.Interface

	cashDesc := func(pl1, pl2 player.Interface) bool {
		return pl1.Cash() > pl2.Cash()
	}

	for _, pl := range g.players {
		classification = append(classification, pl)
	}
	player.By(cashDesc).Sort(classification)
	return classification
}

func (g *Game) StatusName() string {
	return g.state.Name()
}
<commit_msg>Removed status name function and added board getter<commit_after>// Package game manages the flow and status of the game
package acquire

import (
	"errors"
	"github.com/svera/acquire/board"
	"github.com/svera/acquire/corporation"
	"github.com/svera/acquire/fsm"
	"github.com/svera/acquire/player"
	"github.com/svera/acquire/tile"
	"github.com/svera/acquire/tileset"
)

const (
	// ActionNotAllowed is an error returned when action not allowed at current state
	ActionNotAllowed = "action_not_allowed"
	// StockSharesNotBuyable is an error returned when stock shares from a corporation not on board are not buyable
	StockSharesNotBuyable = "stock_shares_not_buyable"
	// NotEnoughStockShares is an error returned when not enough stock shares of a corporation to buy
	NotEnoughStockShares = "not_enough_stock_shares"
	// TileTemporaryUnplayable is an error returned when tile temporarily unplayable
	TileTemporaryUnplayable = "tile_temporary_unplayable"
	// TilePermanentlyUnplayable is an error returned when tile permanently unplayable
	TilePermanentlyUnplayable = "tile_permanently_unplayable"
	// NotEnoughCash is an error returned when player has not enough cash to buy stock shares
	NotEnoughCash = "not_enough_cash"
	// TooManyStockSharesToBuy is an error returned when player can not buy more than 3 stock shares per turn
	TooManyStockSharesToBuy = "too_many_stock_shares_to_buy"
	// CorpNamesNotUnique is an error returned when some corporation names are repeated
	CorpNamesNotUnique = "corp_names_not_unique"
	// WrongNumberCorpsClass is an error returned when corporations classes do not fit rules
	WrongNumberCorpsClass = "wrong_number_corps_class"
	// CorporationAlreadyOnBoard is an error returned when corporation is already on board and cannot be founded
	CorporationAlreadyOnBoard = "corporation_already_on_board"
	// WrongNumberPlayers is an error returned when there must be between 3 and 6 players
	WrongNumberPlayers = "wrong_number_players"
	// NoCorporationSharesOwned is an error returned when player does not own stock shares of a certain corporation
	NoCorporationSharesOwned = "no_corporation_shares_owned"
	// NotEnoughCorporationSharesOwned is an error returned when player does not own enough stock shares of a certain corporation
	NotEnoughCorporationSharesOwned = "not_enough_corporation_shares_owned"
	// TileNotOnHand is an error returned when player does not have tile on hand
	TileNotOnHand = "tile_not_on_hand"
	// NotAnAcquirerCorporation is an error returned when corporation is not the acquirer in a merge
	NotAnAcquirerCorporation = "not_an_acquirer_corporation"
	// TradeAmountNotEven is an error returned when number of stock shares is not even in a trade
	TradeAmountNotEven = "trade_amount_not_even"

	totalCorporations      = 7
	endGameCorporationSize = 41
)

// Game stores state of game elements and provides methods to control game flow
type Game struct {
	board               board.Interface
	state               fsm.State
	players             []player.Interface
	corporations        [7]corporation.Interface
	tileset             tileset.Interface
	currentPlayerNumber int
	newCorpTiles        []tile.Interface
	mergeCorps          map[string][]corporation.Interface
	sellTradePlayers    []int
	lastPlayedTile      tile.Interface
	turn                int
	endGameClaimed      bool
	// When in sell_trade state, the current player is stored here temporary as the turn
	// is passed to all defunct corporations stockholders
	frozenPlayer int
}

// New initialises a new Acquire game
func New(
	board board.Interface, players []player.Interface, corporations [7]corporation.Interface, tileset tileset.Interface) (*Game, error) {
	if len(players) < 3 || len(players) > 6 {
		return nil, errors.New(WrongNumberPlayers)
	}
	if !areNamesUnique(corporations) {
		return nil, errors.New(CorpNamesNotUnique)
	}
	if !isNumberOfCorpsPerClassRight(corporations) {
		return nil, errors.New(WrongNumberCorpsClass)
	}
	gm := Game{
		board:               board,
		players:             players,
		corporations:        corporations,
		tileset:             tileset,
		currentPlayerNumber: 0,
		turn:                1,
		state:               &fsm.PlayTile{},
		endGameClaimed:      false,
	}
	for _, pl := range gm.players {
		gm.giveInitialTileset(pl)
	}

	return &gm, nil
}

// Check that the passed corporations have unique names
func areNamesUnique(corporations [7]corporation.Interface) bool {
	for i, corp1 := range corporations {
		if i < len(corporations)-1 {
			for _, corp2 := range corporations[i+1:] {
				if corp1.Name() == corp2.Name() {
					return false
				}
			}
		}
	}
	return true
}

// Check that the number of corporations per class is right
func isNumberOfCorpsPerClassRight(corporations [7]corporation.Interface) bool {
	corpsPerClass := [3]int{0, 0, 0}
	for _, corp := range corporations {
		corpsPerClass[corp.Class()]++
	}
	if corpsPerClass[0] != 2 || corpsPerClass[1] != 3 || corpsPerClass[2] != 2 {
		return false
	}
	return true
}

// Initialises player hand of tiles
func (g *Game) giveInitialTileset(plyr player.Interface) {
	for i := 0; i < 6; i++ {
		tile, _ := g.tileset.Draw()
		plyr.PickTile(tile)
	}
}

// AreEndConditionsReached check if game end conditions are reached
func (g *Game) AreEndConditionsReached() bool {
	active := g.getActiveCorporations()
	if len(active) == 0 {
		return false
	}
	for _, corp := range active {
		if corp.Size() >= endGameCorporationSize {
			return true
		}
		if !corp.IsSafe() {
			return false
		}
	}
	return true
}

// Returns all corporations on the board
func (g *Game) getActiveCorporations() []corporation.Interface {
	active := []corporation.Interface{}
	for _, corp := range g.corporations {
		if corp.IsActive() {
			active = append(active, corp)
		}
	}
	return active
}

// Returns true if a tile is permanently unplayable, that is,
// that putting it on the board would merge two or more safe corporations
func (g *Game) isTileUnplayable(tl tile.Interface) bool {
	adjacents := g.board.AdjacentCells(tl)
	safeNeighbours := 0
	for _, adjacent := range adjacents {
		if adjacent.Owner().Type() == "corporation" && adjacent.Owner().(corporation.Interface).IsSafe() {
			safeNeighbours++
		}
		if safeNeighbours == 2 {
			return true
		}
	}
	return false
}

// Returns true if a tile is temporarily unplayable, that is,
// that putting it on the board would create an 8th corporation
func (g *Game) isTileTemporaryUnplayable(tl tile.Interface) bool {
	if len(g.getActiveCorporations()) < totalCorporations {
		return false
	}
	adjacents := g.board.AdjacentCells(tl)
	for _, adjacent := range adjacents {
		if adjacent.Owner().Type() == "unincorporated" {
			return true
		}
	}
	return false
}

// Player returns player with passed number
func (g *Game) Player(playerNumber int) player.Interface {
	return g.players[playerNumber]
}

// CurrentPlayer returns player currently in play
func (g *Game) CurrentPlayer() player.Interface {
	return g.players[g.currentPlayerNumber]
}

// PlayTile puts the given tile on board and triggers related actions
func (g *Game) PlayTile(tl tile.Interface) error {
	if g.state.Name() != "PlayTile" {
		return errors.New(ActionNotAllowed)
	}
	if g.isTileTemporaryUnplayable(tl) {
		return errors.New(TileTemporaryUnplayable)
	}
	if !g.CurrentPlayer().HasTile(tl) {
		return errors.New(TileNotOnHand)
	}

	g.CurrentPlayer().DiscardTile(tl)
	g.lastPlayedTile = tl

	if merge, mergeCorps := g.board.TileMergeCorporations(tl); merge {
		g.mergeCorps = mergeCorps
		if g.isMergeTied() {
			g.state = g.state.ToUntieMerge()
		} else {
			for _, corp := range mergeCorps["defunct"] {
				g.payBonuses(corp)
			}
			g.sellTradePlayers = g.stockholders(mergeCorps["defunct"])
			g.frozenPlayer = g.currentPlayerNumber
			g.setCurrentPlayer(g.nextSellTradePlayer())
			g.state = g.state.ToSellTrade()
		}
	} else if found, tiles := g.board.TileFoundCorporation(tl); found {
		g.state = g.state.ToFoundCorp()
		g.newCorpTiles = tiles
	} else if grow, tiles, corp := g.board.TileGrowCorporation(tl); grow {
		g.growCorporation(corp, tiles)
		g.state = g.state.ToBuyStock()
	} else {
		g.board.PutTile(tl)
		g.state = g.state.ToBuyStock()
	}
	return nil
}

// Returns players who are shareholders of at least one of the passed companies
// starting from the current one in play (mergemaker)
func (g *Game) stockholders(corporations []corporation.Interface) []int {
	shareholders := []int{}
	index := g.currentPlayerNumber
	for _ = range g.players {
		for _, corp := range g.corporations {
			if g.players[index].Shares(corp) > 0 {
				shareholders = append(shareholders, index)
				break
			}
		}
		index++
		if index == len(g.players) {
			index = 0
		}
	}
	return shareholders
}

// Sets player currently in play
func (g *Game) setCurrentPlayer(number int) *Game {
	g.currentPlayerNumber = number
	return g
}

// FoundCorporation founds a new corporation
func (g *Game) FoundCorporation(corp corporation.Interface) error {
	if g.state.Name() != "FoundCorp" {
		return errors.New(ActionNotAllowed)
	}
	if corp.IsActive() {
		return errors.New(CorporationAlreadyOnBoard)
	}
	g.board.SetOwner(corp, g.newCorpTiles)
	corp.Grow(len(g.newCorpTiles))
	g.newCorpTiles = []tile.Interface{}
	g.getFounderStockShare(g.CurrentPlayer(), corp)
	g.state = g.state.ToBuyStock()
	return nil
}

// Receive a free stock share from a rencently found corporation, if it has
// remaining shares available
// TODO this should trigger an event warning that no founder stock share will be given
// of the founded corporation has no stock shares left
func (g *Game) getFounderStockShare(pl player.Interface, corp corporation.Interface) {
	if corp.Stock() > 0 {
		corp.RemoveStock(1)
		pl.AddShares(corp, 1)
	}
}

// Makes a corporation grow with the passed tiles
func (g *Game) growCorporation(corp corporation.Interface, tiles []tile.Interface) {
	g.board.SetOwner(corp, tiles)
	corp.Grow(len(tiles))
}

// Increases the number which specifies the current player
func (g *Game) nextPlayer() {
	g.currentPlayerNumber++
	if g.currentPlayerNumber == len(g.players) {
		g.currentPlayerNumber = 0
		g.turn++
	}
}

// Turn returns the current turn number
func (g *Game) Turn() int {
	return g.turn
}

// claimEndGame allows the current player to claim end game
// This can be done at any time. After announcing that the game is over,
// the player may finish the turn.
func (g *Game) ClaimEndGame() *Game {
	if g.AreEndConditionsReached() {
		g.endGameClaimed = true
	}
	return g
}

// Classification returns the players list ordered by cash,
// which is the metric used to know game's final classification
func (g *Game) Classification() []player.Interface {
	var classification []player.Interface

	cashDesc := func(pl1, pl2 player.Interface) bool {
		return pl1.Cash() > pl2.Cash()
	}

	for _, pl := range g.players {
		classification = append(classification, pl)
	}
	player.By(cashDesc).Sort(classification)
	return classification
}

func (g *Game) Board() board.Interface {
	return g.board
}
<|endoftext|>


<commit_before>package main

import (
  "fmt"
)

type SetupRules struct {
  Steps []string
}

var TicTacToeSetup = SetupRules{
  []string{
    "Draw 3x3 grid",
    "Choose X or O",
  },
}

func main() {
  fmt.Println("game on")
  for _,r := range TicTacToeSetup.Steps {
    fmt.Println(r)
  }
}
<commit_msg>Some steps must be done by each player<commit_after>package main

import (
  "fmt"
)

type SetupRules struct {
  Steps []SetupStep
}

type SetupStep struct {
  Description string
  Arity string
}

var TicTacToeSetup = SetupRules{
  []SetupStep{
    {"Draw 3x3 grid", "Once"},
    {"Choose X or O", "Each player"},
  },
}

func main() {
  fmt.Println("game on")
  for _,r := range TicTacToeSetup.Steps {
    fmt.Printf("%s\t%s\n", r.Description, r.Arity)
  }
}
<|endoftext|>


<commit_before>package cmd

import (
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

// Version is the current version of the buffalo binary
const Version = "v0.11.0"

func init() {
	decorate("version", versionCmd)
	RootCmd.AddCommand(versionCmd)
}

var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Print the version number of buffalo",
	Long:  `All software has versions.  This is buffalo's.`,
	Run: func(c *cobra.Command, args []string) {
		logrus.Infof("Buffalo version is: %s\n", Version)
	},
	// needed to override the root level pre-run func
	PersistentPreRunE: func(c *cobra.Command, args []string) error {
		return nil
	},
}
<commit_msg>version bump<commit_after>package cmd

import (
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

// Version is the current version of the buffalo binary
const Version = "v0.11.1"

func init() {
	decorate("version", versionCmd)
	RootCmd.AddCommand(versionCmd)
}

var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Print the version number of buffalo",
	Long:  `All software has versions.  This is buffalo's.`,
	Run: func(c *cobra.Command, args []string) {
		logrus.Infof("Buffalo version is: %s\n", Version)
	},
	// needed to override the root level pre-run func
	PersistentPreRunE: func(c *cobra.Command, args []string) error {
		return nil
	},
}
<|endoftext|>


<commit_before>package nats

import (
	"net"
	"testing"
	"sync"
	"nats/test"
)

type testConnection struct {
	// Network pipe
	nc, ns net.Conn

	// Test server
	s *test.TestServer

	// Test connection
	c *Connection

	// Channel to receive the return value of c.Run()
	ec chan error

	// WaitGroup to join goroutines after every test
	sync.WaitGroup
}

func (tc *testConnection) Setup(t *testing.T) {
	tc.nc, tc.ns = net.Pipe()
	tc.s = test.NewTestServer(t, tc.ns)
	tc.c = NewConnection(tc.nc)
	tc.ec = make(chan error, 1)

	tc.Add(1)
	go func() {
		tc.ec <- tc.c.Run()
		tc.Done()
	}()
}

func (tc *testConnection) Teardown() {
	// Close test server
	tc.s.Close()

	// Wait for goroutines
	tc.Wait()
}

func TestConnectionPongOnPing(t *testing.T) {
	var tc testConnection

	tc.Setup(t)

	// Write PING
	tc.s.AssertWrite("PING\r\n")

	// Read PONG
	tc.s.AssertRead("PONG\r\n")

	tc.Teardown()
}

func TestConnectionPingWhenConnected(t *testing.T) {
	var tc testConnection

	tc.Setup(t)

	tc.Add(1)
	go func() {
		tc.s.AssertRead("PING\r\n")
		tc.s.AssertWrite("PONG\r\n")
		tc.Done()
	}()

	var ok bool = tc.c.Ping()
	if !ok {
		t.Errorf("Expected OK")
	}

	tc.Teardown()
}

func TestConnectionPingWhenDisconnected(t *testing.T) {
	var tc testConnection

	tc.Setup(t)

	tc.Add(1)
	go func() {
		tc.s.Close()
		tc.Done()
	}()

	var ok bool = tc.c.Ping()
	if ok {
		t.Errorf("Expected not OK")
	}

	tc.Teardown()
}

func TestConnectionPingWhenDisconnectedMidway(t *testing.T) {
	var tc testConnection

	tc.Setup(t)

	tc.Add(1)
	go func() {
		tc.s.AssertRead("PING\r\n")
		tc.s.Close()
		tc.Done()
	}()

	var ok bool = tc.c.Ping()
	if ok {
		t.Errorf("Expected not OK")
	}

	tc.Teardown()
}
<commit_msg>Run() should return nil when connection is explicitly stopped<commit_after>package nats

import (
	"net"
	"testing"
	"sync"
	"nats/test"
)

type testConnection struct {
	// Network pipe
	nc, ns net.Conn

	// Test server
	s *test.TestServer

	// Test connection
	c *Connection

	// Channel to receive the return value of c.Run()
	ec chan error

	// WaitGroup to join goroutines after every test
	sync.WaitGroup
}

func (tc *testConnection) Setup(t *testing.T) {
	tc.nc, tc.ns = net.Pipe()
	tc.s = test.NewTestServer(t, tc.ns)
	tc.c = NewConnection(tc.nc)
	tc.ec = make(chan error, 1)

	tc.Add(1)
	go func() {
		tc.ec <- tc.c.Run()
		tc.Done()
	}()
}

func (tc *testConnection) Teardown() {
	// Close test server
	tc.s.Close()

	// Wait for goroutines
	tc.Wait()
}

func TestConnectionReturnNilOnStop(t *testing.T) {
	var tc testConnection

	tc.Setup(t)

	// Stop from goroutine
	tc.Add(1)
	go func() {
		tc.c.Stop()
		tc.Done()
	}()

	e, ok := <-tc.ec
	if !ok {
		t.Errorf("Expected OK")
	}

	if e != nil {
		t.Error(e)
	}

	tc.Teardown()
}

func TestConnectionPongOnPing(t *testing.T) {
	var tc testConnection

	tc.Setup(t)

	// Write PING
	tc.s.AssertWrite("PING\r\n")

	// Read PONG
	tc.s.AssertRead("PONG\r\n")

	tc.Teardown()
}

func TestConnectionPingWhenConnected(t *testing.T) {
	var tc testConnection

	tc.Setup(t)

	tc.Add(1)
	go func() {
		tc.s.AssertRead("PING\r\n")
		tc.s.AssertWrite("PONG\r\n")
		tc.Done()
	}()

	var ok bool = tc.c.Ping()
	if !ok {
		t.Errorf("Expected OK")
	}

	tc.Teardown()
}

func TestConnectionPingWhenDisconnected(t *testing.T) {
	var tc testConnection

	tc.Setup(t)

	tc.Add(1)
	go func() {
		tc.s.Close()
		tc.Done()
	}()

	var ok bool = tc.c.Ping()
	if ok {
		t.Errorf("Expected not OK")
	}

	tc.Teardown()
}

func TestConnectionPingWhenDisconnectedMidway(t *testing.T) {
	var tc testConnection

	tc.Setup(t)

	tc.Add(1)
	go func() {
		tc.s.AssertRead("PING\r\n")
		tc.s.Close()
		tc.Done()
	}()

	var ok bool = tc.c.Ping()
	if ok {
		t.Errorf("Expected not OK")
	}

	tc.Teardown()
}
<|endoftext|>


<commit_before>// Copyright (c) 2016, Sean Treadway, SoundCloud Ltd.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Source code and contact info at http://github.com/streadway/amqp

// +build integration

package amqp

import (
	"crypto/tls"
	"net"
	"sync"
	"testing"
)

func TestChannelOpenOnAClosedConnectionFails(t *testing.T) {
	conn := integrationConnection(t, "channel on close")

	conn.Close()

	if _, err := conn.Channel(); err != ErrClosed {
		t.Fatalf("channel.open on a closed connection %#v is expected to fail", conn)
	}
}

// TestChannelOpenOnAClosedConnectionFails_ReleasesAllocatedChannel ensures the
// channel allocated is released if opening the channel fails.
func TestChannelOpenOnAClosedConnectionFails_ReleasesAllocatedChannel(t *testing.T) {
	conn := integrationConnection(t, "releases channel allocation")
	conn.Close()

	before := len(conn.channels)

	if _, err := conn.Channel(); err != ErrClosed {
		t.Fatalf("channel.open on a closed connection %#v is expected to fail", conn)
	}

	if len(conn.channels) != before {
		t.Fatalf("channel.open failed, but the allocated channel was not released")
	}
}

// TestRaceBetweenChannelAndConnectionClose ensures allocating a new channel
// does not race with shutting the connection down.
//
// See https://github.com/streadway/amqp/issues/251 - thanks to jmalloc for the
// test case.
func TestRaceBetweenChannelAndConnectionClose(t *testing.T) {
	conn := integrationConnection(t, "allocation/shutdown race")

	go conn.Close()
	for i := 0; i < 10; i++ {
		go func() {
			ch, err := conn.Channel()
			if err == nil {
				ch.Close()
			}
		}()
	}
}

func TestQueueDeclareOnAClosedConnectionFails(t *testing.T) {
	conn := integrationConnection(t, "queue declare on close")
	ch, _ := conn.Channel()

	conn.Close()

	if _, err := ch.QueueDeclare("an example", false, false, false, false, nil); err != ErrClosed {
		t.Fatalf("queue.declare on a closed connection %#v is expected to return ErrClosed, returned: %#v", conn, err)
	}
}

func TestConcurrentClose(t *testing.T) {
	const concurrency = 32

	conn := integrationConnection(t, "concurrent close")
	defer conn.Close()

	wg := sync.WaitGroup{}
	wg.Add(concurrency)
	for i := 0; i < concurrency; i++ {
		go func() {
			defer wg.Done()

			err := conn.Close()

			if err == nil {
				t.Log("first concurrent close was successful")
				return
			}

			if err == ErrClosed {
				t.Log("later concurrent close were successful and returned ErrClosed")
				return
			}

			// BUG(st) is this really acceptable? we got a net.OpError before the
			// connection was marked as closed means a race condition between the
			// network connection and handshake state. It should be a package error
			// returned.
			if _, neterr := err.(*net.OpError); neterr {
				t.Logf("unknown net.OpError during close, ignoring: %+v", err)
				return
			}

			// A different/protocol error occurred indicating a race or missed condition
			if _, other := err.(*Error); other {
				t.Fatalf("Expected no error, or ErrClosed, or a net.OpError from conn.Close(), got %#v (%s) of type %T", err, err, err)
			}
		}()
	}
	wg.Wait()
}

// TestPlaintextDialTLS esnures amqp:// connections succeed when using DialTLS.
func TestPlaintextDialTLS(t *testing.T) {
	uri, err := ParseURI(integrationURLFromEnv())
	if err != nil {
		t.Fatalf("parse URI error: %s", err)
	}

	// We can only test when we have a plaintext listener
	if uri.Scheme != "amqp" {
		t.Skip("requires server listening for plaintext connections")
	}

	conn, err := DialTLS(uri.String(), &tls.Config{MinVersion: tls.VersionTLS12})
	if err != nil {
		t.Fatalf("unexpected dial error, got %v", err)
	}
	conn.Close()
}
<commit_msg>Add test case for race between channel.shutdown() and any callers of ch.send()<commit_after>// Copyright (c) 2016, Sean Treadway, SoundCloud Ltd.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Source code and contact info at http://github.com/streadway/amqp

// +build integration

package amqp

import (
	"crypto/tls"
	"net"
	"sync"
	"testing"
)

func TestChannelOpenOnAClosedConnectionFails(t *testing.T) {
	conn := integrationConnection(t, "channel on close")

	conn.Close()

	if _, err := conn.Channel(); err != ErrClosed {
		t.Fatalf("channel.open on a closed connection %#v is expected to fail", conn)
	}
}

// TestChannelOpenOnAClosedConnectionFails_ReleasesAllocatedChannel ensures the
// channel allocated is released if opening the channel fails.
func TestChannelOpenOnAClosedConnectionFails_ReleasesAllocatedChannel(t *testing.T) {
	conn := integrationConnection(t, "releases channel allocation")
	conn.Close()

	before := len(conn.channels)

	if _, err := conn.Channel(); err != ErrClosed {
		t.Fatalf("channel.open on a closed connection %#v is expected to fail", conn)
	}

	if len(conn.channels) != before {
		t.Fatalf("channel.open failed, but the allocated channel was not released")
	}
}

// TestRaceBetweenChannelAndConnectionClose ensures allocating a new channel
// does not race with shutting the connection down.
//
// See https://github.com/streadway/amqp/issues/251 - thanks to jmalloc for the
// test case.
func TestRaceBetweenChannelAndConnectionClose(t *testing.T) {
	conn := integrationConnection(t, "allocation/shutdown race")

	go conn.Close()
	for i := 0; i < 10; i++ {
		go func() {
			ch, err := conn.Channel()
			if err == nil {
				ch.Close()
			}
		}()
	}
}

// TestRaceBetweenChannelShutdownAndSend ensures closing a channel
// (channel.shutdown) does not race with calling channel.send() from any other
// goroutines.
//
// See https://github.com/streadway/amqp/pull/253#issuecomment-292464811 for
// more details - thanks to jmalloc again.
func TestRaceBetweenChannelShutdownAndSend(t *testing.T) {
	conn := integrationConnection(t, "channel close/send race")
	defer conn.Close()

	ch, _ := conn.Channel()

	go ch.Close()
	for i := 0; i < 10; i++ {
		go func() {
			// ch.Ack calls ch.send() internally.
			ch.Ack(42, false)
		}()
	}
}

func TestQueueDeclareOnAClosedConnectionFails(t *testing.T) {
	conn := integrationConnection(t, "queue declare on close")
	ch, _ := conn.Channel()

	conn.Close()

	if _, err := ch.QueueDeclare("an example", false, false, false, false, nil); err != ErrClosed {
		t.Fatalf("queue.declare on a closed connection %#v is expected to return ErrClosed, returned: %#v", conn, err)
	}
}

func TestConcurrentClose(t *testing.T) {
	const concurrency = 32

	conn := integrationConnection(t, "concurrent close")
	defer conn.Close()

	wg := sync.WaitGroup{}
	wg.Add(concurrency)
	for i := 0; i < concurrency; i++ {
		go func() {
			defer wg.Done()

			err := conn.Close()

			if err == nil {
				t.Log("first concurrent close was successful")
				return
			}

			if err == ErrClosed {
				t.Log("later concurrent close were successful and returned ErrClosed")
				return
			}

			// BUG(st) is this really acceptable? we got a net.OpError before the
			// connection was marked as closed means a race condition between the
			// network connection and handshake state. It should be a package error
			// returned.
			if _, neterr := err.(*net.OpError); neterr {
				t.Logf("unknown net.OpError during close, ignoring: %+v", err)
				return
			}

			// A different/protocol error occurred indicating a race or missed condition
			if _, other := err.(*Error); other {
				t.Fatalf("Expected no error, or ErrClosed, or a net.OpError from conn.Close(), got %#v (%s) of type %T", err, err, err)
			}
		}()
	}
	wg.Wait()
}

// TestPlaintextDialTLS esnures amqp:// connections succeed when using DialTLS.
func TestPlaintextDialTLS(t *testing.T) {
	uri, err := ParseURI(integrationURLFromEnv())
	if err != nil {
		t.Fatalf("parse URI error: %s", err)
	}

	// We can only test when we have a plaintext listener
	if uri.Scheme != "amqp" {
		t.Skip("requires server listening for plaintext connections")
	}

	conn, err := DialTLS(uri.String(), &tls.Config{MinVersion: tls.VersionTLS12})
	if err != nil {
		t.Fatalf("unexpected dial error, got %v", err)
	}
	conn.Close()
}
<|endoftext|>


<commit_before>package consensus

import (
	"sync"
)

// The zero address and the zero currency are convenience variables.
var (
	ZeroUnlockHash = UnlockHash{0}
	ZeroCurrency   = NewCurrency64(0)
)

// The State is the object responsible for tracking the current status of the
// blockchain. It accepts blocks and maintains an understanding of competing
// forks. The State object is responsible for maintaining consensus.
type State struct {
	// The blockRoot is the block node that contains the genesis block, which
	// is the foundation for all other blocks. blockNodes form a tree, each
	// having many children and pointing back to the parent.
	blockRoot *blockNode

	// badBlocks and blockMap keep track of known blocks. badBlocks keeps track
	// of invalid blocks and is used exclusively for DoS prevention. blockMap
	// points only to blocks that exist in some competing fork within the
	// blockchain.
	badBlocks map[BlockID]struct{}
	blockMap  map[BlockID]*blockNode

	// currentPath and currentBlockID track which blocks are currently accepted
	// as the longest known blockchain.
	currentBlockID BlockID
	currentPath    map[BlockHeight]BlockID

	// These are the consensus variables, referred to as the 'consensus set'.
	// All nodes on the network which have the same current path will have an
	// identical consensus set. (Anything else is an error)
	//
	// The siafundPool counts how many siacoins have been taken from file
	// contracts in total. As transactions and blocks are added to the
	// currentPath, the siafundPool may only increase in size. The Currency
	// type is not typically allowed to overflow, however in the case of the
	// siafund pool it is okay.
	//
	// siacoinOutputs, fileContracts, and siafundOutputs are all atomic items
	// within the state. Either they exist or they don't. Two objects with the
	// same id will always have the same contents. This makes tracking diffs in
	// the consensus set very easy.
	//
	// delayedSiacoinOutputs are siacoin outputs that have been created in a
	// block but are not yet allowed to be spent. Miner payouts for example are
	// not allowed to be spent right away. All of the delayed outputs that get
	// created at a certain height are put into a list. When 'MaturityDelay'
	// blocks have passed, the outputs are moved into the 'siafundOutputs' map.
	siafundPool           Currency
	siacoinOutputs        map[SiacoinOutputID]SiacoinOutput
	fileContracts         map[FileContractID]FileContract
	siafundOutputs        map[SiafundOutputID]SiafundOutput
	delayedSiacoinOutputs map[BlockHeight]map[SiacoinOutputID]SiacoinOutput

	// Per convention, all exported functions in the consensus package can be
	// called concurrently. The state mutex helps to orchestrate thread safety.
	// To keep things simple, the entire state was chosen to have a single
	// mutex, as opposed to putting frequently accessed fields under separate
	// mutexes. The performance advantage was decided to be not worth the
	// complexity tradeoff.
	mu sync.RWMutex
}

// CreateGenesisState will create the state that contains the genesis block and
// nothing else. The unexported version of this function takes a timestamp and
// some unlock hashes for the siafunds as input, which makes testing easier.
func createGenesisState(genesisTime Timestamp, fundUnlockHash UnlockHash, claimUnlockHash UnlockHash) (s *State) {
	// Create a new state and initialize the maps.
	s = &State{
		badBlocks:             make(map[BlockID]struct{}),
		blockMap:              make(map[BlockID]*blockNode),
		currentPath:           make(map[BlockHeight]BlockID),
		siacoinOutputs:        make(map[SiacoinOutputID]SiacoinOutput),
		fileContracts:         make(map[FileContractID]FileContract),
		siafundOutputs:        make(map[SiafundOutputID]SiafundOutput),
		delayedSiacoinOutputs: make(map[BlockHeight]map[SiacoinOutputID]SiacoinOutput),
	}

	// Create the genesis block and add it as the BlockRoot.
	genesisBlock := Block{
		Timestamp: genesisTime,
	}
	s.blockRoot = &blockNode{
		block:  genesisBlock,
		target: RootTarget,
		depth:  RootDepth,
	}
	s.blockMap[genesisBlock.ID()] = s.blockRoot

	// Fill out the consensus information for the genesis block.
	s.currentBlockID = genesisBlock.ID()
	s.currentPath[BlockHeight(0)] = genesisBlock.ID()
	s.siacoinOutputs[genesisBlock.MinerPayoutID(0)] = SiacoinOutput{
		Value:      CalculateCoinbase(0),
		UnlockHash: ZeroUnlockHash,
	}
	s.siafundOutputs[SiafundOutputID{0}] = SiafundOutput{
		Value:           NewCurrency64(SiafundCount),
		UnlockHash:      fundUnlockHash,
		ClaimUnlockHash: claimUnlockHash,
	}

	return
}

// CreateGenesisState returns the state that contains the genesis block and
// nothing else. The exported version of this function uses the genesis
// constants.
func CreateGenesisState() (s *State) {
	return createGenesisState(GenesisTimestamp, GenesisSiafundUnlockHash, GenesisClaimUnlockHash)
}

// RLock will readlock the state.
//
// TODO: Add a safety timer which will auto-unlock if the readlock is held for
// more than a second. (panic in debug mode)
func (s *State) RLock() {
	s.mu.RLock()
}

// RUnlock will readunlock the state.
//
// TODO: when the safety timer is added to RLock, add a timer disabler to
// RUnlock to prevent too many unlocks from being called.
func (s *State) RUnlock() {
	s.mu.RUnlock()
}
<commit_msg>review docstrings in state.go<commit_after>package consensus

import (
	"sync"
)

// The ZeroUnlockHash and ZeroCurrency are convenience variables.
var (
	ZeroUnlockHash = UnlockHash{0}
	ZeroCurrency   = NewCurrency64(0)
)

// The State is the object responsible for tracking the current status of the
// blockchain. Broadly speaking, it is responsible for maintaining consensus.
// It accepts blocks and constructs a blockchain, forking when necessary.
type State struct {
	// The blockRoot is the block node that contains the genesis block.
	blockRoot *blockNode

	// blockMap and badBlocks keep track of seen blocks. blockMap holds all
	// valid blocks, including those not on the main blockchain. badBlocks
	// is a "blacklist" of blocks known to be invalid.
	blockMap  map[BlockID]*blockNode
	badBlocks map[BlockID]struct{}

	// currentPath and currentBlockID track which blocks are currently accepted
	// as the longest known blockchain.
	currentPath    map[BlockHeight]BlockID
	currentBlockID BlockID

	// These are the consensus variables, referred to as the "consensus set."
	// All nodes with the same current path must have the same consensus set.
	//
	// The siafundPool tracks the total number of siacoins that have been
	// taxed from file contracts. Unless a reorg occurs, the siafundPool
	// should never decrease.
	//
	// siacoinOutputs, fileContracts, and siafundOutputs keep track of the
	// unspent outputs and active contracts present in the current path. If an
	// output is spent or a contract expires, it is removed from the consensus
	// set. These objects may also be removed in the event of a reorg.
	//
	// delayedSiacoinOutputs are siacoin outputs that have been created in a
	// block, but are not allowed to be spent until a certain height. When
	// that height is reached, they are moved to the siacoinOutputs map.
	siafundPool           Currency
	siacoinOutputs        map[SiacoinOutputID]SiacoinOutput
	fileContracts         map[FileContractID]FileContract
	siafundOutputs        map[SiafundOutputID]SiafundOutput
	delayedSiacoinOutputs map[BlockHeight]map[SiacoinOutputID]SiacoinOutput

	// Per convention, all exported functions in the consensus package can be
	// called concurrently. The state mutex helps to orchestrate thread safety.
	// To keep things simple, the entire state was chosen to have a single
	// mutex, as opposed to putting frequently accessed fields under separate
	// mutexes. The performance advantage was decided to be not worth the
	// complexity tradeoff.
	mu sync.RWMutex
}

// createGenesisState returns a State containing only the genesis block. It
// takes arguments instead of using global constants to make testing easier.
func createGenesisState(genesisTime Timestamp, fundUnlockHash UnlockHash, claimUnlockHash UnlockHash) (s *State) {
	// Create a new state and initialize the maps.
	s = &State{
		blockMap:              make(map[BlockID]*blockNode),
		badBlocks:             make(map[BlockID]struct{}),
		currentPath:           make(map[BlockHeight]BlockID),
		siacoinOutputs:        make(map[SiacoinOutputID]SiacoinOutput),
		fileContracts:         make(map[FileContractID]FileContract),
		siafundOutputs:        make(map[SiafundOutputID]SiafundOutput),
		delayedSiacoinOutputs: make(map[BlockHeight]map[SiacoinOutputID]SiacoinOutput),
	}

	// Create the genesis block and add it as the BlockRoot.
	genesisBlock := Block{
		Timestamp: genesisTime,
	}
	s.blockRoot = &blockNode{
		block:  genesisBlock,
		target: RootTarget,
		depth:  RootDepth,
	}
	s.blockMap[genesisBlock.ID()] = s.blockRoot

	// Fill out the consensus information for the genesis block.
	s.currentPath[0] = genesisBlock.ID()
	s.currentBlockID = genesisBlock.ID()
	s.siacoinOutputs[genesisBlock.MinerPayoutID(0)] = SiacoinOutput{
		Value:      CalculateCoinbase(0),
		UnlockHash: ZeroUnlockHash,
	}
	s.siafundOutputs[SiafundOutputID{0}] = SiafundOutput{
		Value:           NewCurrency64(SiafundCount),
		UnlockHash:      fundUnlockHash,
		ClaimUnlockHash: claimUnlockHash,
	}

	return
}

// CreateGenesisState returns a State containing only the genesis block.
func CreateGenesisState() (s *State) {
	return createGenesisState(GenesisTimestamp, GenesisSiafundUnlockHash, GenesisClaimUnlockHash)
}

// RLock will readlock the state.
//
// TODO: Add a safety timer which will auto-unlock if the readlock is held for
// more than a second. (panic in debug mode)
func (s *State) RLock() {
	s.mu.RLock()
}

// RUnlock will readunlock the state.
//
// TODO: when the safety timer is added to RLock, add a timer disabler to
// RUnlock to prevent too many unlocks from being called.
func (s *State) RUnlock() {
	s.mu.RUnlock()
}
<|endoftext|>


<commit_before>/*
   Copyright The containerd Authors.

   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 content

import (
	"context"
	"io"
	"io/ioutil"
	"math/rand"
	"sync"
	"time"

	"github.com/containerd/containerd/errdefs"
	"github.com/opencontainers/go-digest"
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
	"github.com/pkg/errors"
)

var bufPool = sync.Pool{
	New: func() interface{} {
		buffer := make([]byte, 1<<20)
		return &buffer
	},
}

// NewReader returns a io.Reader from a ReaderAt
func NewReader(ra ReaderAt) io.Reader {
	rd := io.NewSectionReader(ra, 0, ra.Size())
	return rd
}

// ReadBlob retrieves the entire contents of the blob from the provider.
//
// Avoid using this for large blobs, such as layers.
func ReadBlob(ctx context.Context, provider Provider, desc ocispec.Descriptor) ([]byte, error) {
	ra, err := provider.ReaderAt(ctx, desc)
	if err != nil {
		return nil, err
	}
	defer ra.Close()

	p := make([]byte, ra.Size())

	n, err := ra.ReadAt(p, 0)
	if err == io.EOF {
		if int64(n) != ra.Size() {
			err = io.ErrUnexpectedEOF
		} else {
			err = nil
		}
	}
	return p, err
}

// WriteBlob writes data with the expected digest into the content store. If
// expected already exists, the method returns immediately and the reader will
// not be consumed.
//
// This is useful when the digest and size are known beforehand.
//
// Copy is buffered, so no need to wrap reader in buffered io.
func WriteBlob(ctx context.Context, cs Ingester, ref string, r io.Reader, desc ocispec.Descriptor, opts ...Opt) error {
	cw, err := OpenWriter(ctx, cs, WithRef(ref), WithDescriptor(desc))
	if err != nil {
		if !errdefs.IsAlreadyExists(err) {
			return errors.Wrap(err, "failed to open writer")
		}

		return nil // all ready present
	}
	defer cw.Close()

	return Copy(ctx, cw, r, desc.Size, desc.Digest, opts...)
}

// OpenWriter opens a new writer for the given reference, retrying if the writer
// is locked until the reference is available or returns an error.
func OpenWriter(ctx context.Context, cs Ingester, opts ...WriterOpt) (Writer, error) {
	var (
		cw    Writer
		err   error
		retry = 16
	)
	for {
		cw, err = cs.Writer(ctx, opts...)
		if err != nil {
			if !errdefs.IsUnavailable(err) {
				return nil, err
			}

			// TODO: Check status to determine if the writer is active,
			// continue waiting while active, otherwise return lock
			// error or abort. Requires asserting for an ingest manager

			select {
			case <-time.After(time.Millisecond * time.Duration(rand.Intn(retry))):
				if retry < 2048 {
					retry = retry << 1
				}
				continue
			case <-ctx.Done():
				// Propagate lock error
				return nil, err
			}

		}
		break
	}

	return cw, err
}

// Copy copies data with the expected digest from the reader into the
// provided content store writer. This copy commits the writer.
//
// This is useful when the digest and size are known beforehand. When
// the size or digest is unknown, these values may be empty.
//
// Copy is buffered, so no need to wrap reader in buffered io.
func Copy(ctx context.Context, cw Writer, r io.Reader, size int64, expected digest.Digest, opts ...Opt) error {
	ws, err := cw.Status()
	if err != nil {
		return errors.Wrap(err, "failed to get status")
	}

	if ws.Offset > 0 {
		r, err = seekReader(r, ws.Offset, size)
		if err != nil {
			return errors.Wrapf(err, "unable to resume write to %v", ws.Ref)
		}
	}

	if _, err := copyWithBuffer(cw, r); err != nil {
		return errors.Wrap(err, "failed to copy")
	}

	if err := cw.Commit(ctx, size, expected, opts...); err != nil {
		if !errdefs.IsAlreadyExists(err) {
			return errors.Wrapf(err, "failed commit on ref %q", ws.Ref)
		}
	}

	return nil
}

// CopyReaderAt copies to a writer from a given reader at for the given
// number of bytes. This copy does not commit the writer.
func CopyReaderAt(cw Writer, ra ReaderAt, n int64) error {
	ws, err := cw.Status()
	if err != nil {
		return err
	}

	_, err = copyWithBuffer(cw, io.NewSectionReader(ra, ws.Offset, n))
	return err
}

// CopyReader copies to a writer from a given reader, returning
// the number of bytes copied.
// Note: if the writer has a non-zero offset, the total number
// of bytes read may be greater than those copied if the reader
// is not an io.Seeker.
// This copy does not commit the writer.
func CopyReader(cw Writer, r io.Reader) (int64, error) {
	ws, err := cw.Status()
	if err != nil {
		return 0, errors.Wrap(err, "failed to get status")
	}

	if ws.Offset > 0 {
		r, err = seekReader(r, ws.Offset, 0)
		if err != nil {
			return 0, errors.Wrapf(err, "unable to resume write to %v", ws.Ref)
		}
	}

	return copyWithBuffer(cw, r)
}

// seekReader attempts to seek the reader to the given offset, either by
// resolving `io.Seeker`, by detecting `io.ReaderAt`, or discarding
// up to the given offset.
func seekReader(r io.Reader, offset, size int64) (io.Reader, error) {
	// attempt to resolve r as a seeker and setup the offset.
	seeker, ok := r.(io.Seeker)
	if ok {
		nn, err := seeker.Seek(offset, io.SeekStart)
		if nn != offset {
			return nil, errors.Wrapf(err, "failed to seek to offset %v", offset)
		}

		if err != nil {
			return nil, err
		}

		return r, nil
	}

	// ok, let's try io.ReaderAt!
	readerAt, ok := r.(io.ReaderAt)
	if ok && size > offset {
		sr := io.NewSectionReader(readerAt, offset, size)
		return sr, nil
	}

	// well then, let's just discard up to the offset
	n, err := copyWithBuffer(ioutil.Discard, io.LimitReader(r, offset))
	if err != nil {
		return nil, errors.Wrap(err, "failed to discard to offset")
	}
	if n != offset {
		return nil, errors.Errorf("unable to discard to offset")
	}

	return r, nil
}

func copyWithBuffer(dst io.Writer, src io.Reader) (written int64, err error) {
	bufRef := bufPool.Get().(*[]byte)
	defer bufPool.Put(bufRef)
	buf := *bufRef
	for {
		nr, er := io.ReadAtLeast(src, buf, len(buf))
		if nr > 0 {
			nw, ew := dst.Write(buf[0:nr])
			if nw > 0 {
				written += int64(nw)
			}
			if ew != nil {
				err = ew
				break
			}
			if nr != nw {
				err = io.ErrShortWrite
				break
			}
		}
		if er != nil {
			if er != io.EOF && er != io.ErrUnexpectedEOF {
				err = er
			}
			break
		}
	}
	return
}
<commit_msg>replicate io.Copy optimizations<commit_after>/*
   Copyright The containerd Authors.

   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 content

import (
	"context"
	"io"
	"io/ioutil"
	"math/rand"
	"sync"
	"time"

	"github.com/containerd/containerd/errdefs"
	"github.com/opencontainers/go-digest"
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
	"github.com/pkg/errors"
)

var bufPool = sync.Pool{
	New: func() interface{} {
		buffer := make([]byte, 1<<20)
		return &buffer
	},
}

// NewReader returns a io.Reader from a ReaderAt
func NewReader(ra ReaderAt) io.Reader {
	rd := io.NewSectionReader(ra, 0, ra.Size())
	return rd
}

// ReadBlob retrieves the entire contents of the blob from the provider.
//
// Avoid using this for large blobs, such as layers.
func ReadBlob(ctx context.Context, provider Provider, desc ocispec.Descriptor) ([]byte, error) {
	ra, err := provider.ReaderAt(ctx, desc)
	if err != nil {
		return nil, err
	}
	defer ra.Close()

	p := make([]byte, ra.Size())

	n, err := ra.ReadAt(p, 0)
	if err == io.EOF {
		if int64(n) != ra.Size() {
			err = io.ErrUnexpectedEOF
		} else {
			err = nil
		}
	}
	return p, err
}

// WriteBlob writes data with the expected digest into the content store. If
// expected already exists, the method returns immediately and the reader will
// not be consumed.
//
// This is useful when the digest and size are known beforehand.
//
// Copy is buffered, so no need to wrap reader in buffered io.
func WriteBlob(ctx context.Context, cs Ingester, ref string, r io.Reader, desc ocispec.Descriptor, opts ...Opt) error {
	cw, err := OpenWriter(ctx, cs, WithRef(ref), WithDescriptor(desc))
	if err != nil {
		if !errdefs.IsAlreadyExists(err) {
			return errors.Wrap(err, "failed to open writer")
		}

		return nil // all ready present
	}
	defer cw.Close()

	return Copy(ctx, cw, r, desc.Size, desc.Digest, opts...)
}

// OpenWriter opens a new writer for the given reference, retrying if the writer
// is locked until the reference is available or returns an error.
func OpenWriter(ctx context.Context, cs Ingester, opts ...WriterOpt) (Writer, error) {
	var (
		cw    Writer
		err   error
		retry = 16
	)
	for {
		cw, err = cs.Writer(ctx, opts...)
		if err != nil {
			if !errdefs.IsUnavailable(err) {
				return nil, err
			}

			// TODO: Check status to determine if the writer is active,
			// continue waiting while active, otherwise return lock
			// error or abort. Requires asserting for an ingest manager

			select {
			case <-time.After(time.Millisecond * time.Duration(rand.Intn(retry))):
				if retry < 2048 {
					retry = retry << 1
				}
				continue
			case <-ctx.Done():
				// Propagate lock error
				return nil, err
			}

		}
		break
	}

	return cw, err
}

// Copy copies data with the expected digest from the reader into the
// provided content store writer. This copy commits the writer.
//
// This is useful when the digest and size are known beforehand. When
// the size or digest is unknown, these values may be empty.
//
// Copy is buffered, so no need to wrap reader in buffered io.
func Copy(ctx context.Context, cw Writer, r io.Reader, size int64, expected digest.Digest, opts ...Opt) error {
	ws, err := cw.Status()
	if err != nil {
		return errors.Wrap(err, "failed to get status")
	}

	if ws.Offset > 0 {
		r, err = seekReader(r, ws.Offset, size)
		if err != nil {
			return errors.Wrapf(err, "unable to resume write to %v", ws.Ref)
		}
	}

	if _, err := copyWithBuffer(cw, r); err != nil {
		return errors.Wrap(err, "failed to copy")
	}

	if err := cw.Commit(ctx, size, expected, opts...); err != nil {
		if !errdefs.IsAlreadyExists(err) {
			return errors.Wrapf(err, "failed commit on ref %q", ws.Ref)
		}
	}

	return nil
}

// CopyReaderAt copies to a writer from a given reader at for the given
// number of bytes. This copy does not commit the writer.
func CopyReaderAt(cw Writer, ra ReaderAt, n int64) error {
	ws, err := cw.Status()
	if err != nil {
		return err
	}

	_, err = copyWithBuffer(cw, io.NewSectionReader(ra, ws.Offset, n))
	return err
}

// CopyReader copies to a writer from a given reader, returning
// the number of bytes copied.
// Note: if the writer has a non-zero offset, the total number
// of bytes read may be greater than those copied if the reader
// is not an io.Seeker.
// This copy does not commit the writer.
func CopyReader(cw Writer, r io.Reader) (int64, error) {
	ws, err := cw.Status()
	if err != nil {
		return 0, errors.Wrap(err, "failed to get status")
	}

	if ws.Offset > 0 {
		r, err = seekReader(r, ws.Offset, 0)
		if err != nil {
			return 0, errors.Wrapf(err, "unable to resume write to %v", ws.Ref)
		}
	}

	return copyWithBuffer(cw, r)
}

// seekReader attempts to seek the reader to the given offset, either by
// resolving `io.Seeker`, by detecting `io.ReaderAt`, or discarding
// up to the given offset.
func seekReader(r io.Reader, offset, size int64) (io.Reader, error) {
	// attempt to resolve r as a seeker and setup the offset.
	seeker, ok := r.(io.Seeker)
	if ok {
		nn, err := seeker.Seek(offset, io.SeekStart)
		if nn != offset {
			return nil, errors.Wrapf(err, "failed to seek to offset %v", offset)
		}

		if err != nil {
			return nil, err
		}

		return r, nil
	}

	// ok, let's try io.ReaderAt!
	readerAt, ok := r.(io.ReaderAt)
	if ok && size > offset {
		sr := io.NewSectionReader(readerAt, offset, size)
		return sr, nil
	}

	// well then, let's just discard up to the offset
	n, err := copyWithBuffer(ioutil.Discard, io.LimitReader(r, offset))
	if err != nil {
		return nil, errors.Wrap(err, "failed to discard to offset")
	}
	if n != offset {
		return nil, errors.Errorf("unable to discard to offset")
	}

	return r, nil
}

func copyWithBuffer(dst io.Writer, src io.Reader) (written int64, err error) {
	// If the reader has a WriteTo method, use it to do the copy.
	// Avoids an allocation and a copy.
	if wt, ok := src.(io.WriterTo); ok {
		return wt.WriteTo(dst)
	}
	// Similarly, if the writer has a ReadFrom method, use it to do the copy.
	if rt, ok := dst.(io.ReaderFrom); ok {
		return rt.ReadFrom(src)
	}
	bufRef := bufPool.Get().(*[]byte)
	defer bufPool.Put(bufRef)
	buf := *bufRef
	for {
		nr, er := io.ReadAtLeast(src, buf, len(buf))
		if nr > 0 {
			nw, ew := dst.Write(buf[0:nr])
			if nw > 0 {
				written += int64(nw)
			}
			if ew != nil {
				err = ew
				break
			}
			if nr != nw {
				err = io.ErrShortWrite
				break
			}
		}
		if er != nil {
			if er != io.EOF && er != io.ErrUnexpectedEOF {
				err = er
			}
			break
		}
	}
	return
}
<|endoftext|>