<commit_before>package sgload

import (
	"fmt"
	"strings"
	"sync"
	"time"

	sgreplicate "github.com/couchbaselabs/sg-replicate"
)

type Reader struct {
	Agent
	SGChannels      []string // The Sync Gateway channels this reader is assigned to pull from
	NumDocsExpected int      // The total number of docs this reader is expected to pull
	BatchSize       int      // The number of docs to pull in batch (_changes feed and bulk_get)
}

func NewReader(wg *sync.WaitGroup, ID int, u UserCred, d DataStore, batchsize int) *Reader {

	return &Reader{
		Agent: Agent{
			FinishedWg: wg,
			UserCred:   u,
			ID:         ID,
			DataStore:  d,
			BatchSize:  batchsize,
		},
	}
}

func (r *Reader) SetChannels(sgChannels []string) {
	r.SGChannels = sgChannels
}

func (r *Reader) SetNumDocsExpected(n int) {
	r.NumDocsExpected = n
}

func (r *Reader) SetBatchSize(batchSize int) {
	r.BatchSize = batchSize
}

func (r *Reader) Run() {

	defer r.FinishedWg.Done()

	if r.CreateDataStoreUser == true {

		if err := r.DataStore.CreateUser(r.UserCred, r.SGChannels); err != nil {
			panic(fmt.Sprintf("Error creating user in datastore.  User: %v, Err: %v", r.UserCred, err))
		}
	}

	numDocsPulled := 0
	since := StringSincer{}
	result := pullMoreDocsResult{}
	var err error

	for {

		if numDocsPulled > r.NumDocsExpected {
			panic(fmt.Sprintf("Reader was only expected to pull %d docs, but pulled %d.", r.NumDocsExpected, numDocsPulled))
		}

		logger.Info("Reader pulled docs", "agent.ID", r.ID, "numDocsPulled", numDocsPulled, "numDocsExpected", r.NumDocsExpected)

		if numDocsPulled == r.NumDocsExpected {
			// reader finished!
			logger.Info("Reader finished", "agent.ID", r.ID, "numDocsPulled", numDocsPulled, "numDocsExpected", r.NumDocsExpected)
			return
		}

		result, err = r.pullMoreDocs(since)
		if err != nil {
			panic(fmt.Sprintf("Got error getting changes: %v", err))
		}
		since = result.since
		numDocsPulled += result.numDocsPulled

	}

}

type pullMoreDocsResult struct {
	since         StringSincer
	numDocsPulled int
}

func (r *Reader) pullMoreDocs(since Sincer) (pullMoreDocsResult, error) {

	// Create a retry sleeper which controls how many times to retry
	// and how long to wait in between retries
	numRetries := 8
	sleepMsBetweenRetry := 500
	retrySleeper := CreateDoublingSleeperFunc(numRetries, sleepMsBetweenRetry)

	// Create retry worker that knows how to do actual work
	retryWorker := func() (shouldRetry bool, err error, value interface{}) {

		result := pullMoreDocsResult{}

		changes, newSince, err := r.DataStore.Changes(since, r.BatchSize)
		if err != nil {
			return false, err, result
		}

		if len(changes.Results) == 0 {
			logger.Warn("No changes pulled", "agent.ID", r.ID, "since", since)
			return true, nil, result
		}
		if newSince.Equals(since) {
			logger.Warn("Since value should have changed", "agent.ID", r.ID, "since", since, "newsince", newSince)
			return true, nil, result
		}

		// Strip out any changes with id "id":"_user/*"
		// since they are user docs and we don't care about them
		changes = stripUserDocChanges(changes)

		bulkGetRequest := getBulkGetRequest(changes)

		err = r.DataStore.BulkGetDocuments(bulkGetRequest)
		if err != nil {
			return false, err, result
		}

		result.since = newSince.(StringSincer)
		result.numDocsPulled = len(bulkGetRequest.Docs)
		return false, nil, result

	}

	// Invoke the retry worker / sleeper combo in a loop
	err, workerReturnVal := RetryLoop("pullMoreDocs", retryWorker, retrySleeper)
	if err != nil {
		return pullMoreDocsResult{}, err
	}

	return workerReturnVal.(pullMoreDocsResult), nil

}

func getBulkGetRequest(changes sgreplicate.Changes) sgreplicate.BulkGetRequest {

	bulkDocsRequest := sgreplicate.BulkGetRequest{}
	docs := []sgreplicate.DocumentRevisionPair{}
	for _, change := range changes.Results {
		docRevPair := sgreplicate.DocumentRevisionPair{}
		docRevPair.Id = change.Id
		docRevPair.Revision = change.ChangedRevs[0].Revision
		docs = append(docs, docRevPair)
	}
	bulkDocsRequest.Docs = docs
	return bulkDocsRequest

}

func stripUserDocChanges(changes sgreplicate.Changes) (changesStripped sgreplicate.Changes) {
	changesStripped.LastSequence = changes.LastSequence

	for _, change := range changes.Results {
		if strings.Contains(change.Id, "_user") {
			continue
		}
		changesStripped.Results = append(changesStripped.Results, change)

	}

	return changesStripped
}

// A retry sleeper is called back by the retry loop and passed
// the current retryCount, and should return the amount of milliseconds
// that the retry should sleep.
type RetrySleeper func(retryCount int) (shouldContinue bool, timeTosleepMs int)

// A RetryWorker encapsulates the work being done in a Retry Loop.  The shouldRetry
// return value determines whether the worker will retry, regardless of the err value.
// If the worker has exceeded it's retry attempts, then it will not be called again
// even if it returns shouldRetry = true.
type RetryWorker func() (shouldRetry bool, err error, value interface{})

func RetryLoop(description string, worker RetryWorker, sleeper RetrySleeper) (error, interface{}) {

	numAttempts := 1

	for {
		shouldRetry, err, value := worker()
		if !shouldRetry {
			if err != nil {
				return err, nil
			}
			return nil, value
		}
		shouldContinue, sleepMs := sleeper(numAttempts)
		if !shouldContinue {
			if err == nil {
				err = fmt.Errorf("RetryLoop for %v giving up after %v attempts", description, numAttempts)
			}
			logger.Warn("RetryLoop giving up", "description", description, "numAttempts", numAttempts)
			return err, value
		}
		logger.Warn("RetryLoop will retry soon", "description", description, "sleepMs", sleepMs)

		<-time.After(time.Millisecond * time.Duration(sleepMs))

		numAttempts += 1

	}
}

// Create a RetrySleeper that will double the retry time on every iteration and
// use the given parameters
func CreateDoublingSleeperFunc(maxNumAttempts, initialTimeToSleepMs int) RetrySleeper {

	timeToSleepMs := initialTimeToSleepMs

	sleeper := func(numAttempts int) (bool, int) {
		if numAttempts > maxNumAttempts {
			return false, -1
		}
		if numAttempts > 1 {
			timeToSleepMs *= 2
		}
		return true, timeToSleepMs
	}
	return sleeper

}
<commit_msg>Refactor isFinished()<commit_after>package sgload

import (
	"fmt"
	"strings"
	"sync"
	"time"

	sgreplicate "github.com/couchbaselabs/sg-replicate"
)

type Reader struct {
	Agent
	SGChannels      []string // The Sync Gateway channels this reader is assigned to pull from
	NumDocsExpected int      // The total number of docs this reader is expected to pull
	BatchSize       int      // The number of docs to pull in batch (_changes feed and bulk_get)
}

func NewReader(wg *sync.WaitGroup, ID int, u UserCred, d DataStore, batchsize int) *Reader {

	return &Reader{
		Agent: Agent{
			FinishedWg: wg,
			UserCred:   u,
			ID:         ID,
			DataStore:  d,
			BatchSize:  batchsize,
		},
	}
}

func (r *Reader) SetChannels(sgChannels []string) {
	r.SGChannels = sgChannels
}

func (r *Reader) SetNumDocsExpected(n int) {
	r.NumDocsExpected = n
}

func (r *Reader) SetBatchSize(batchSize int) {
	r.BatchSize = batchSize
}

func (r *Reader) Run() {

	defer r.FinishedWg.Done()

	if r.CreateDataStoreUser == true {

		if err := r.DataStore.CreateUser(r.UserCred, r.SGChannels); err != nil {
			panic(fmt.Sprintf("Error creating user in datastore.  User: %v, Err: %v", r.UserCred, err))
		}
	}

	numDocsPulled := 0
	since := StringSincer{}
	result := pullMoreDocsResult{}
	var err error

	for {

		if r.isFinished(numDocsPulled) {
			logger.Info("Reader finished", "agent.ID", r.ID, "numDocs", r.NumDocsExpected)
			break
		}
		result, err = r.pullMoreDocs(since)
		if err != nil {
			panic(fmt.Sprintf("Got error getting changes: %v", err))
		}
		since = result.since
		numDocsPulled += result.numDocsPulled

	}

}

func (r *Reader) isFinished(numDocsPulled int) bool {

	logger.Info("Reader checking if finished", "agent.ID", r.ID, "numDocsPulled", numDocsPulled, "numDocsExpected", r.NumDocsExpected)

	switch {
	case numDocsPulled > r.NumDocsExpected:
		panic(fmt.Sprintf("Reader was only expected to pull %d docs, but pulled %d.", r.NumDocsExpected, numDocsPulled))
	case numDocsPulled == r.NumDocsExpected:
		return true
	default:
		return false
	}

}

type pullMoreDocsResult struct {
	since         StringSincer
	numDocsPulled int
}

func (r *Reader) pullMoreDocs(since Sincer) (pullMoreDocsResult, error) {

	// Create a retry sleeper which controls how many times to retry
	// and how long to wait in between retries
	numRetries := 7
	sleepMsBetweenRetry := 500
	retrySleeper := CreateDoublingSleeperFunc(numRetries, sleepMsBetweenRetry)

	// Create retry worker that knows how to do actual work
	retryWorker := func() (shouldRetry bool, err error, value interface{}) {

		result := pullMoreDocsResult{}

		changes, newSince, err := r.DataStore.Changes(since, r.BatchSize)
		if err != nil {
			return false, err, result
		}

		if len(changes.Results) == 0 {
			logger.Warn("No changes pulled", "agent.ID", r.ID, "since", since)
			return true, nil, result
		}
		if newSince.Equals(since) {
			logger.Warn("Since value should have changed", "agent.ID", r.ID, "since", since, "newsince", newSince)
			return true, nil, result
		}

		// Strip out any changes with id "id":"_user/*"
		// since they are user docs and we don't care about them
		changes = stripUserDocChanges(changes)

		bulkGetRequest := getBulkGetRequest(changes)

		err = r.DataStore.BulkGetDocuments(bulkGetRequest)
		if err != nil {
			return false, err, result
		}

		result.since = newSince.(StringSincer)
		result.numDocsPulled = len(bulkGetRequest.Docs)
		return false, nil, result

	}

	// Invoke the retry worker / sleeper combo in a loop
	err, workerReturnVal := RetryLoop("pullMoreDocs", retryWorker, retrySleeper)
	if err != nil {
		return pullMoreDocsResult{}, err
	}

	return workerReturnVal.(pullMoreDocsResult), nil

}

func getBulkGetRequest(changes sgreplicate.Changes) sgreplicate.BulkGetRequest {

	bulkDocsRequest := sgreplicate.BulkGetRequest{}
	docs := []sgreplicate.DocumentRevisionPair{}
	for _, change := range changes.Results {
		docRevPair := sgreplicate.DocumentRevisionPair{}
		docRevPair.Id = change.Id
		docRevPair.Revision = change.ChangedRevs[0].Revision
		docs = append(docs, docRevPair)
	}
	bulkDocsRequest.Docs = docs
	return bulkDocsRequest

}

func stripUserDocChanges(changes sgreplicate.Changes) (changesStripped sgreplicate.Changes) {
	changesStripped.LastSequence = changes.LastSequence

	for _, change := range changes.Results {
		if strings.Contains(change.Id, "_user") {
			continue
		}
		changesStripped.Results = append(changesStripped.Results, change)

	}

	return changesStripped
}

// A retry sleeper is called back by the retry loop and passed
// the current retryCount, and should return the amount of milliseconds
// that the retry should sleep.
type RetrySleeper func(retryCount int) (shouldContinue bool, timeTosleepMs int)

// A RetryWorker encapsulates the work being done in a Retry Loop.  The shouldRetry
// return value determines whether the worker will retry, regardless of the err value.
// If the worker has exceeded it's retry attempts, then it will not be called again
// even if it returns shouldRetry = true.
type RetryWorker func() (shouldRetry bool, err error, value interface{})

func RetryLoop(description string, worker RetryWorker, sleeper RetrySleeper) (error, interface{}) {

	numAttempts := 1

	for {
		shouldRetry, err, value := worker()
		if !shouldRetry {
			if err != nil {
				return err, nil
			}
			return nil, value
		}
		shouldContinue, sleepMs := sleeper(numAttempts)
		if !shouldContinue {
			if err == nil {
				err = fmt.Errorf("RetryLoop for %v giving up after %v attempts", description, numAttempts)
			}
			logger.Warn("RetryLoop giving up", "description", description, "numAttempts", numAttempts)
			return err, value
		}
		logger.Warn("RetryLoop will retry soon", "description", description, "sleepMs", sleepMs)

		<-time.After(time.Millisecond * time.Duration(sleepMs))

		numAttempts += 1

	}
}

// Create a RetrySleeper that will double the retry time on every iteration and
// use the given parameters
func CreateDoublingSleeperFunc(maxNumAttempts, initialTimeToSleepMs int) RetrySleeper {

	timeToSleepMs := initialTimeToSleepMs

	sleeper := func(numAttempts int) (bool, int) {
		if numAttempts > maxNumAttempts {
			return false, -1
		}
		if numAttempts > 1 {
			timeToSleepMs *= 2
		}
		return true, timeToSleepMs
	}
	return sleeper

}
<|endoftext|>


<commit_before>package shamir

import (
	"crypto/rand"
	"crypto/subtle"
	"fmt"
)

const (
	// ShareOverhead is the byte size overhead of each share
	// when using Split on a secret. This is caused by appending
	// a one byte tag to the share.
	ShareOverhead = 1
)

// polynomial represents a polynomial of arbitrary degree
type polynomial struct {
	coefficients []uint8
}

// makePolynomial constructs a random polynomial of the given
// degree but with the provided intercept value.
func makePolynomial(intercept, degree uint8) (polynomial, error) {
	// Create a wrapper
	p := polynomial{
		coefficients: make([]byte, degree+1),
	}

	// Ensure the intercept is set
	p.coefficients[0] = intercept

	// Assign random co-efficients to the polynomial, ensuring
	// the highest order co-efficient is non-zero
	for p.coefficients[degree] == 0 {
		if _, err := rand.Read(p.coefficients[1:]); err != nil {
			return p, err
		}
	}
	return p, nil
}

// evaluate returns the value of the polynomial for the given x
func (p *polynomial) evaluate(x uint8) uint8 {
	// Special case the origin
	if x == 0 {
		return p.coefficients[0]
	}

	// Compute the polynomial value using Horner's method.
	degree := len(p.coefficients) - 1
	out := p.coefficients[degree]
	for i := degree - 1; i >= 0; i-- {
		coeff := p.coefficients[i]
		out = add(mult(out, x), coeff)
	}
	return out
}

// interpolatePolynomial takes N sample points and returns
// the value at a given x using a lagrange interpolation.
func interpolatePolynomial(x_samples, y_samples []uint8, x uint8) uint8 {
	limit := len(x_samples)
	var result, basis uint8
	for i := 0; i < limit; i++ {
		basis = 1
		for j := 0; j < limit; j++ {
			if i == j {
				continue
			}
			num := add(x, x_samples[j])
			denom := add(x_samples[i], x_samples[j])
			term := div(num, denom)
			basis = mult(basis, term)
		}
		group := mult(y_samples[i], basis)
		result = add(result, group)
	}
	return result
}

// div divides two numbers in GF(2^8)
func div(a, b uint8) uint8 {
	if b == 0 {
		// leaks some timing information but we don't care anyways as this
		// should never happen, hence the panic
		panic("divide by zero")
	}

	var goodVal, zero uint8
	log_a := logTable[a]
	log_b := logTable[b]
	diff := (int(log_a) - int(log_b)) % 255
	if diff < 0 {
		diff += 255
	}

	ret := expTable[diff]

	// Ensure we return zero if a is zero but aren't subject to timing attacks
	goodVal = ret

	if subtle.ConstantTimeByteEq(a, 0) == 1 {
		ret = zero
	} else {
		ret = goodVal
	}

	return ret
}

// mult multiplies two numbers in GF(2^8)
func mult(a, b uint8) (out uint8) {
	var goodVal, zero uint8
	log_a := logTable[a]
	log_b := logTable[b]
	sum := (int(log_a) + int(log_b)) % 255

	ret := expTable[sum]

	// Ensure we return zero if either a or be are zero but aren't subject to
	// timing attacks
	goodVal = ret

	if subtle.ConstantTimeByteEq(a, 0) == 1 {
		ret = zero
	} else {
		ret = goodVal
	}

	if subtle.ConstantTimeByteEq(b, 0) == 1 {
		ret = zero
	} else {
		// This operation does not do anything logically useful. It
		// only ensures a constant number of assignments to thwart
		// timing attacks.
		goodVal = zero
	}

	return ret
}

// add combines two numbers in GF(2^8)
// This can also be used for subtraction since it is symmetric.
func add(a, b uint8) uint8 {
	return a ^ b
}

// Split takes an arbitrarily long secret and generates a `parts`
// number of shares, `threshold` of which are required to reconstruct
// the secret. The parts and threshold must be at least 2, and less
// than 256. The returned shares are each one byte longer than the secret
// as they attach a tag used to reconstruct the secret.
func Split(secret []byte, parts, threshold int) ([][]byte, error) {
	// Sanity check the input
	if parts < threshold {
		return nil, fmt.Errorf("parts cannot be less than threshold")
	}
	if parts > 255 {
		return nil, fmt.Errorf("parts cannot exceed 255")
	}
	if threshold < 2 {
		return nil, fmt.Errorf("threshold must be at least 2")
	}
	if threshold > 255 {
		return nil, fmt.Errorf("threshold cannot exceed 255")
	}
	if len(secret) == 0 {
		return nil, fmt.Errorf("cannot split an empty secret")
	}

	// Allocate the output array, initialize the final byte
	// of the output with the offset. The representation of each
	// output is {y1, y2, .., yN, x}.
	out := make([][]byte, parts)
	for idx := range out {
		out[idx] = make([]byte, len(secret)+1)
		out[idx][len(secret)] = uint8(idx) + 1
	}

	// Construct a random polynomial for each byte of the secret.
	// Because we are using a field of size 256, we can only represent
	// a single byte as the intercept of the polynomial, so we must
	// use a new polynomial for each byte.
	for idx, val := range secret {
		p, err := makePolynomial(val, uint8(threshold-1))
		if err != nil {
			return nil, fmt.Errorf("failed to generate polynomial: %v", err)
		}

		// Generate a `parts` number of (x,y) pairs
		// We cheat by encoding the x value once as the final index,
		// so that it only needs to be stored once.
		for i := 0; i < parts; i++ {
			x := uint8(i) + 1
			y := p.evaluate(x)
			out[i][idx] = y
		}
	}

	// Return the encoded secrets
	return out, nil
}

// Combine is used to reverse a Split and reconstruct a secret
// once a `threshold` number of parts are available.
func Combine(parts [][]byte) ([]byte, error) {
	// Verify enough parts provided
	if len(parts) < 2 {
		return nil, fmt.Errorf("less than two parts cannot be used to reconstruct the secret")
	}

	// Verify the parts are all the same length
	firstPartLen := len(parts[0])
	if firstPartLen < 2 {
		return nil, fmt.Errorf("parts must be at least two bytes")
	}
	for i := 1; i < len(parts); i++ {
		if len(parts[i]) != firstPartLen {
			return nil, fmt.Errorf("all parts must be the same length")
		}
	}

	// Create a buffer to store the reconstructed secret
	secret := make([]byte, firstPartLen-1)

	// Buffer to store the samples
	x_samples := make([]uint8, len(parts))
	y_samples := make([]uint8, len(parts))

	// Set the x value for each sample and ensure no x_sample values are the same,
	// otherwise div() can be unhappy
	checkMap := map[byte]bool{}
	for i, part := range parts {
		samp := part[firstPartLen-1]
		if exists := checkMap[samp]; exists {
			return nil, fmt.Errorf("duplicate part detected")
		}
		checkMap[samp] = true
		x_samples[i] = samp
	}

	// Reconstruct each byte
	for idx := range secret {
		// Set the y value for each sample
		for i, part := range parts {
			y_samples[i] = part[idx]
		}

		// Interpolte the polynomial and compute the value at 0
		val := interpolatePolynomial(x_samples, y_samples, 0)

		// Evaluate the 0th value to get the intercept
		secret[idx] = val
	}
	return secret, nil
}
<commit_msg>Don't exclude 0 from the set of valid polynomials in Shamir. This leads to a potential (although extremely trivial) amount of information leakage.<commit_after>package shamir

import (
	"crypto/rand"
	"crypto/subtle"
	"fmt"
)

const (
	// ShareOverhead is the byte size overhead of each share
	// when using Split on a secret. This is caused by appending
	// a one byte tag to the share.
	ShareOverhead = 1
)

// polynomial represents a polynomial of arbitrary degree
type polynomial struct {
	coefficients []uint8
}

// makePolynomial constructs a random polynomial of the given
// degree but with the provided intercept value.
func makePolynomial(intercept, degree uint8) (polynomial, error) {
	// Create a wrapper
	p := polynomial{
		coefficients: make([]byte, degree+1),
	}

	// Ensure the intercept is set
	p.coefficients[0] = intercept

	// Assign random co-efficients to the polynomial
	if _, err := rand.Read(p.coefficients[1:]); err != nil {
		return p, err
	}

	return p, nil
}

// evaluate returns the value of the polynomial for the given x
func (p *polynomial) evaluate(x uint8) uint8 {
	// Special case the origin
	if x == 0 {
		return p.coefficients[0]
	}

	// Compute the polynomial value using Horner's method.
	degree := len(p.coefficients) - 1
	out := p.coefficients[degree]
	for i := degree - 1; i >= 0; i-- {
		coeff := p.coefficients[i]
		out = add(mult(out, x), coeff)
	}
	return out
}

// interpolatePolynomial takes N sample points and returns
// the value at a given x using a lagrange interpolation.
func interpolatePolynomial(x_samples, y_samples []uint8, x uint8) uint8 {
	limit := len(x_samples)
	var result, basis uint8
	for i := 0; i < limit; i++ {
		basis = 1
		for j := 0; j < limit; j++ {
			if i == j {
				continue
			}
			num := add(x, x_samples[j])
			denom := add(x_samples[i], x_samples[j])
			term := div(num, denom)
			basis = mult(basis, term)
		}
		group := mult(y_samples[i], basis)
		result = add(result, group)
	}
	return result
}

// div divides two numbers in GF(2^8)
func div(a, b uint8) uint8 {
	if b == 0 {
		// leaks some timing information but we don't care anyways as this
		// should never happen, hence the panic
		panic("divide by zero")
	}

	var goodVal, zero uint8
	log_a := logTable[a]
	log_b := logTable[b]
	diff := (int(log_a) - int(log_b)) % 255
	if diff < 0 {
		diff += 255
	}

	ret := expTable[diff]

	// Ensure we return zero if a is zero but aren't subject to timing attacks
	goodVal = ret

	if subtle.ConstantTimeByteEq(a, 0) == 1 {
		ret = zero
	} else {
		ret = goodVal
	}

	return ret
}

// mult multiplies two numbers in GF(2^8)
func mult(a, b uint8) (out uint8) {
	var goodVal, zero uint8
	log_a := logTable[a]
	log_b := logTable[b]
	sum := (int(log_a) + int(log_b)) % 255

	ret := expTable[sum]

	// Ensure we return zero if either a or be are zero but aren't subject to
	// timing attacks
	goodVal = ret

	if subtle.ConstantTimeByteEq(a, 0) == 1 {
		ret = zero
	} else {
		ret = goodVal
	}

	if subtle.ConstantTimeByteEq(b, 0) == 1 {
		ret = zero
	} else {
		// This operation does not do anything logically useful. It
		// only ensures a constant number of assignments to thwart
		// timing attacks.
		goodVal = zero
	}

	return ret
}

// add combines two numbers in GF(2^8)
// This can also be used for subtraction since it is symmetric.
func add(a, b uint8) uint8 {
	return a ^ b
}

// Split takes an arbitrarily long secret and generates a `parts`
// number of shares, `threshold` of which are required to reconstruct
// the secret. The parts and threshold must be at least 2, and less
// than 256. The returned shares are each one byte longer than the secret
// as they attach a tag used to reconstruct the secret.
func Split(secret []byte, parts, threshold int) ([][]byte, error) {
	// Sanity check the input
	if parts < threshold {
		return nil, fmt.Errorf("parts cannot be less than threshold")
	}
	if parts > 255 {
		return nil, fmt.Errorf("parts cannot exceed 255")
	}
	if threshold < 2 {
		return nil, fmt.Errorf("threshold must be at least 2")
	}
	if threshold > 255 {
		return nil, fmt.Errorf("threshold cannot exceed 255")
	}
	if len(secret) == 0 {
		return nil, fmt.Errorf("cannot split an empty secret")
	}

	// Allocate the output array, initialize the final byte
	// of the output with the offset. The representation of each
	// output is {y1, y2, .., yN, x}.
	out := make([][]byte, parts)
	for idx := range out {
		out[idx] = make([]byte, len(secret)+1)
		out[idx][len(secret)] = uint8(idx) + 1
	}

	// Construct a random polynomial for each byte of the secret.
	// Because we are using a field of size 256, we can only represent
	// a single byte as the intercept of the polynomial, so we must
	// use a new polynomial for each byte.
	for idx, val := range secret {
		p, err := makePolynomial(val, uint8(threshold-1))
		if err != nil {
			return nil, fmt.Errorf("failed to generate polynomial: %v", err)
		}

		// Generate a `parts` number of (x,y) pairs
		// We cheat by encoding the x value once as the final index,
		// so that it only needs to be stored once.
		for i := 0; i < parts; i++ {
			x := uint8(i) + 1
			y := p.evaluate(x)
			out[i][idx] = y
		}
	}

	// Return the encoded secrets
	return out, nil
}

// Combine is used to reverse a Split and reconstruct a secret
// once a `threshold` number of parts are available.
func Combine(parts [][]byte) ([]byte, error) {
	// Verify enough parts provided
	if len(parts) < 2 {
		return nil, fmt.Errorf("less than two parts cannot be used to reconstruct the secret")
	}

	// Verify the parts are all the same length
	firstPartLen := len(parts[0])
	if firstPartLen < 2 {
		return nil, fmt.Errorf("parts must be at least two bytes")
	}
	for i := 1; i < len(parts); i++ {
		if len(parts[i]) != firstPartLen {
			return nil, fmt.Errorf("all parts must be the same length")
		}
	}

	// Create a buffer to store the reconstructed secret
	secret := make([]byte, firstPartLen-1)

	// Buffer to store the samples
	x_samples := make([]uint8, len(parts))
	y_samples := make([]uint8, len(parts))

	// Set the x value for each sample and ensure no x_sample values are the same,
	// otherwise div() can be unhappy
	checkMap := map[byte]bool{}
	for i, part := range parts {
		samp := part[firstPartLen-1]
		if exists := checkMap[samp]; exists {
			return nil, fmt.Errorf("duplicate part detected")
		}
		checkMap[samp] = true
		x_samples[i] = samp
	}

	// Reconstruct each byte
	for idx := range secret {
		// Set the y value for each sample
		for i, part := range parts {
			y_samples[i] = part[idx]
		}

		// Interpolte the polynomial and compute the value at 0
		val := interpolatePolynomial(x_samples, y_samples, 0)

		// Evaluate the 0th value to get the intercept
		secret[idx] = val
	}
	return secret, nil
}
<|endoftext|>


<commit_before>//  The MIT License (MIT) - http://opensource.org/licenses/MIT
//
//  Copyright (c) 2014 slowfei
//
//  Create on 2014-08-22
//  Update on 2014-08-22
//  Email  slowfei#foxmail.com
//  Home   http://www.slowfei.com

//
package gosfdoc

import (
	"container/list"
	"os"
	"regexp"
)

/**
 *  file content buffer
 */
type FileBuf struct {
	path     string
	fileInfo os.FileInfo
	buf      []byte
}

/**
 *  new file buffer
 *
 *  @param fileContent
 *  @param replace regexp, replace text to empty(''), call regexp.ReplaceAll func
 */
func NewFileBuf(fileContent []byte, filter *regexp.Regexp) *FileBuf {
	buf := new(FileBuf)
	if nil != filter {
		buf.buf = filter.ReplaceAll(fileContent, nil)
	} else {
		buf.buf = fileContent
	}
	return buf
}

/**
 *  regexp find bytes
 *
 *  @return
 */
func (f *FileBuf) Find(rex *regexp.Regexp) []byte {
	return rex.Find(f.buf)
}

/**
 *  get file path
 *
 *  @return
 */
func (f *FileBuf) Path() string {
	return f.path
}

/**
 *  get file info
 *
 *  @return
 */
func (f *FileBuf) FileInfo() os.FileInfo {
	return f.fileInfo
}

/**
 *  buffer to string
 *
 *  @return
 */
func (f *FileBuf) String() string {
	return string(f.buf)
}

/**
 *  source code file
 */
type CodeFile struct {
	parser      DocParser  // file parser
	docs        []Document // current file public documents
	FileCont    *FileBuf   // file buffer content
	PrivateDoc  bool       // if private document not output
	PrivateCode bool       // if private source code not output
}

/**
 *  source code file list
 */
type CodeFiles struct {
	files *list.List
}

/**
 *  new CodeFiles
 */
func NewCodeFiles() *CodeFiles {
	cf := new(CodeFiles)
	cf.files = list.New()
	return cf
}

/**
 *  add file
 *
 *  @param file
 */
func (c *CodeFiles) addFile(file CodeFile) {
	if nil == c.files {
		c.files = list.New()
	}
	c.files.PushBack(file)
}

/**
 *  each CodeFile
 *
 *  @param `f` func return true continue
 */
func (c *CodeFiles) Each(f func(file CodeFile) bool) {
	if nil == f {
		return
	}
	for e := c.files.Front(); e != nil; e = e.Next() {
		if !f(e.Value.(CodeFile)) {
			break
		}
	}
}

/**
 *  output `content.json`
 */
type ContentJson struct {
	HtmlTitle string // html document title
	DocTitle  string // html top show title
	MenuTitle string // html left menu title
}

/**
 *  package info
 */
type PackageInfo struct {
	Name string // package name plain text
	Desc string // description plain text
}

/**
 *  document struct info
 */
type Document struct {
	SortTag int    // sort tag
	Title   string // title plain text
	Content string // markdown text or plain text
}

/**
 *  preview struct info
 */
type Preview struct {
	SortTag  int       // sort tag
	ShowText string    // show plain text
	Anchor   string    // preferably unique, with the func link
	DescText string    // markdown brief description or implement objects, can empty.
	Children []Preview //
}

/**
 *  body code block struct
 */
type CodeBlock struct {
	SortTag    int    // sort tag
	Title      string // function name or custom title
	Desc       string // description markdown text or plain text
	Code       string // show code text
	SourceLink string // source code link text
	Anchor     string // function anchor text. with the Preview link
}
<commit_msg>增加FileBuf-FinaAll<commit_after>//  The MIT License (MIT) - http://opensource.org/licenses/MIT
//
//  Copyright (c) 2014 slowfei
//
//  Create on 2014-08-22
//  Update on 2014-08-22
//  Email  slowfei#foxmail.com
//  Home   http://www.slowfei.com

//
package gosfdoc

import (
	"container/list"
	"os"
	"regexp"
)

/**
 *  file content buffer
 */
type FileBuf struct {
	path     string
	fileInfo os.FileInfo
	buf      []byte
}

/**
 *  new file buffer
 *
 *  @param fileContent
 *  @param replace regexp, replace text to empty(''), call regexp.ReplaceAll func
 */
func NewFileBuf(fileContent []byte, filter *regexp.Regexp) *FileBuf {
	buf := new(FileBuf)
	if nil != filter {
		buf.buf = filter.ReplaceAll(fileContent, nil)
	} else {
		buf.buf = fileContent
	}
	return buf
}

/**
 *  regexp find bytes
 *
 *  @param `rex`
 *  @return
 */
func (f *FileBuf) Find(rex *regexp.Regexp) []byte {
	return rex.Find(f.buf)
}

/**
 *  regexp find all bytes
 *
 *  @param `rex`
 *  @return
 */
func (f *FileBuf) FinaAll(rex *regexp.Regexp) [][]byte {
	return rex.FindAll(f.buf, -1)
}

/**
 *  get file path
 *
 *  @return
 */
func (f *FileBuf) Path() string {
	return f.path
}

/**
 *  get file info
 *
 *  @return
 */
func (f *FileBuf) FileInfo() os.FileInfo {
	return f.fileInfo
}

/**
 *  buffer to string
 *
 *  @return
 */
func (f *FileBuf) String() string {
	return string(f.buf)
}

/**
 *  source code file
 */
type CodeFile struct {
	parser      DocParser  // file parser
	docs        []Document // current file public documents
	FileCont    *FileBuf   // file buffer content
	PrivateDoc  bool       // if private document not output
	PrivateCode bool       // if private source code not output
}

/**
 *  source code file list
 */
type CodeFiles struct {
	files *list.List
}

/**
 *  new CodeFiles
 */
func NewCodeFiles() *CodeFiles {
	cf := new(CodeFiles)
	cf.files = list.New()
	return cf
}

/**
 *  add file
 *
 *  @param file
 */
func (c *CodeFiles) addFile(file CodeFile) {
	if nil == c.files {
		c.files = list.New()
	}
	c.files.PushBack(file)
}

/**
 *  each CodeFile
 *
 *  @param `f` func return true continue
 */
func (c *CodeFiles) Each(f func(file CodeFile) bool) {
	if nil == f {
		return
	}
	for e := c.files.Front(); e != nil; e = e.Next() {
		if !f(e.Value.(CodeFile)) {
			break
		}
	}
}

/**
 *  output `content.json`
 */
type ContentJson struct {
	HtmlTitle string // html document title
	DocTitle  string // html top show title
	MenuTitle string // html left menu title
}

/**
 *  package info
 */
type PackageInfo struct {
	Name string // package name plain text
	Desc string // description plain text
}

/**
 *  document struct info
 */
type Document struct {
	SortTag int    // sort tag
	Title   string // title plain text
	Content string // markdown text or plain text
}

/**
 *  preview struct info
 */
type Preview struct {
	SortTag  int       // sort tag
	ShowText string    // show plain text
	Anchor   string    // preferably unique, with the func link
	DescText string    // markdown brief description or implement objects, can empty.
	Children []Preview //
}

/**
 *  body code block struct
 */
type CodeBlock struct {
	SortTag    int    // sort tag
	Title      string // function name or custom title
	Desc       string // description markdown text or plain text
	Code       string // show code text
	SourceLink string // source code link text
	Anchor     string // function anchor text. with the Preview link
}
<|endoftext|>


<commit_before>// Copyright (c) 2014, Rob Thornton
// All rights reserved.
// This source code is governed by a Simplied BSD-License. Please see the
// LICENSE included in this distribution for a copy of the full license
// or, if one is not included, you may also find a copy at
// http://opensource.org/licenses/BSD-2-Clause

package scan

import (
	"unicode"

	"github.com/rthornton128/calc/token"
)

type Scanner struct {
	ch      rune
	offset  int
	roffset int
	src     string
	file    *token.File
}

func (s *Scanner) Init(file *token.File, src string) {
	s.file = file
	s.offset, s.roffset = 0, 0
	s.src = src

	s.next()
}

func (s *Scanner) Scan() (lit string, tok token.Token, pos token.Pos) {
	s.skipWhitespace()

	if unicode.IsLetter(s.ch) {
		return s.scanIdentifier()
	}

	if unicode.IsDigit(s.ch) {
		return s.scanNumber()
	}

	ch := s.ch
	lit, pos = string(s.ch), s.file.Pos(s.offset)
	s.next()
	switch ch {
	case '(':
		tok = token.LPAREN
	case ')':
		tok = token.RPAREN
	case ',':
		tok = token.COMMA
	case '+':
		tok = token.ADD
	case '-':
		tok = token.SUB
	case '*':
		tok = token.MUL
	case '/':
		tok = token.QUO
	case '%':
		tok = token.REM
	case '<':
		if s.ch == '=' {
			tok = token.LTE
			s.next()
		} else {
			tok = token.LST
		}
	case '>':
		if s.ch == '=' {
			tok = token.GTE
			s.next()
		} else {
			tok = token.GTT
		}
	case '=':
		if s.ch == '=' {
			tok = token.EQL
			s.next()
		} else {
			tok = token.ASSIGN
		}
	case '!':
		if s.ch == '=' {
			tok = token.NEQ
			s.next()
		} else {
			tok = token.ILLEGAL
		}
	case '&':
		if s.ch == '&' {
			tok = token.AND
			s.next()
		} else {
			tok = token.ILLEGAL
		}
	case '|':
		if s.ch == '|' {
			tok = token.OR
			s.next()
		} else {
			tok = token.ILLEGAL
		}
	case ';':
		s.skipComment()
		s.next()
		return s.Scan()
	default:
		if s.offset >= len(s.src)-1 {
			tok = token.EOF
		} else {
			tok = token.ILLEGAL
		}
	}

	return
}

func (s *Scanner) next() {
	s.ch = rune(0)
	if s.roffset < len(s.src) {
		s.offset = s.roffset
		if s.ch == '\n' || s.offset >= len(s.src)-1 {
			s.file.AddLine(token.Pos(s.offset))
		}
		s.ch = rune(s.src[s.offset])
		s.roffset++
	}
}

func (s *Scanner) scanIdentifier() (string, token.Token, token.Pos) {
	start := s.offset

	for unicode.IsLetter(s.ch) || unicode.IsDigit(s.ch) {
		s.next()
	}
	offset := s.offset
	if s.ch == rune(0) {
		offset++
	}
	return s.src[start:offset], token.IDENT, s.file.Pos(start)
}

func (s *Scanner) scanNumber() (string, token.Token, token.Pos) {
	start := s.offset

	for unicode.IsDigit(s.ch) {
		s.next()
	}
	offset := s.offset
	if s.ch == rune(0) {
		offset++
	}
	return s.src[start:offset], token.INTEGER, s.file.Pos(start)
}

func (s *Scanner) skipComment() {
	for s.ch != '\n' && s.offset < len(s.src)-1 {
		s.next()
	}
}

func (s *Scanner) skipWhitespace() {
	for unicode.IsSpace(s.ch) {
		s.next()
	}
}
<commit_msg>Scanner to lookup keywords<commit_after>// Copyright (c) 2014, Rob Thornton
// All rights reserved.
// This source code is governed by a Simplied BSD-License. Please see the
// LICENSE included in this distribution for a copy of the full license
// or, if one is not included, you may also find a copy at
// http://opensource.org/licenses/BSD-2-Clause

package scan

import (
	"unicode"

	"github.com/rthornton128/calc/token"
)

type Scanner struct {
	ch      rune
	offset  int
	roffset int
	src     string
	file    *token.File
}

func (s *Scanner) Init(file *token.File, src string) {
	s.file = file
	s.offset, s.roffset = 0, 0
	s.src = src

	s.next()
}

func (s *Scanner) Scan() (lit string, tok token.Token, pos token.Pos) {
	s.skipWhitespace()

	if unicode.IsLetter(s.ch) {
		return s.scanIdentifier()
	}

	if unicode.IsDigit(s.ch) {
		return s.scanNumber()
	}

	ch := s.ch
	lit, pos = string(s.ch), s.file.Pos(s.offset)
	s.next()
	switch ch {
	case '(':
		tok = token.LPAREN
	case ')':
		tok = token.RPAREN
	case ',':
		tok = token.COMMA
	case '+':
		tok = token.ADD
	case '-':
		tok = token.SUB
	case '*':
		tok = token.MUL
	case '/':
		tok = token.QUO
	case '%':
		tok = token.REM
	case '<':
		if s.ch == '=' {
			tok = token.LTE
			s.next()
		} else {
			tok = token.LST
		}
	case '>':
		if s.ch == '=' {
			tok = token.GTE
			s.next()
		} else {
			tok = token.GTT
		}
	case '=':
		if s.ch == '=' {
			tok = token.EQL
			s.next()
		} else {
			tok = token.ASSIGN
		}
	case '!':
		if s.ch == '=' {
			tok = token.NEQ
			s.next()
		} else {
			tok = token.ILLEGAL
		}
	case '&':
		if s.ch == '&' {
			tok = token.AND
			s.next()
		} else {
			tok = token.ILLEGAL
		}
	case '|':
		if s.ch == '|' {
			tok = token.OR
			s.next()
		} else {
			tok = token.ILLEGAL
		}
	case ';':
		s.skipComment()
		s.next()
		return s.Scan()
	default:
		if s.offset >= len(s.src)-1 {
			tok = token.EOF
		} else {
			tok = token.ILLEGAL
		}
	}

	return
}

func (s *Scanner) next() {
	s.ch = rune(0)
	if s.roffset < len(s.src) {
		s.offset = s.roffset
		if s.ch == '\n' || s.offset >= len(s.src)-1 {
			s.file.AddLine(token.Pos(s.offset))
		}
		s.ch = rune(s.src[s.offset])
		s.roffset++
	}
}

func (s *Scanner) scanIdentifier() (string, token.Token, token.Pos) {
	start := s.offset

	for unicode.IsLetter(s.ch) || unicode.IsDigit(s.ch) {
		s.next()
	}
	offset := s.offset
	if s.ch == rune(0) {
		offset++
	}
	lit := s.src[start:offset]
	return lit, token.Lookup(lit), s.file.Pos(start)
}

func (s *Scanner) scanNumber() (string, token.Token, token.Pos) {
	start := s.offset

	for unicode.IsDigit(s.ch) {
		s.next()
	}
	offset := s.offset
	if s.ch == rune(0) {
		offset++
	}
	return s.src[start:offset], token.INTEGER, s.file.Pos(start)
}

func (s *Scanner) skipComment() {
	for s.ch != '\n' && s.offset < len(s.src)-1 {
		s.next()
	}
}

func (s *Scanner) skipWhitespace() {
	for unicode.IsSpace(s.ch) {
		s.next()
	}
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "flag"
    "net/http"
)

var (
    port *int = flag.Int("p", 8080, "port to listen for connections")
    address *string = flag.String("a", "127.0.0.1", "address to bind to")
    directory string
)

func main() {
    flag.Parse()

    directory := flag.Arg(0)

    if len(directory) == 0 {
        //No directory specified. Use current directory to host files
        directory = "./"
    }

    http.Handle("/", http.FileServer(http.Dir(directory)))

    fmt.Printf("Starting http server at %s on port %d \n", *address, *port)

    fmt.Println("Hit CTRL-C to stop the server")

    completeAddress := fmt.Sprintf("%s:%d", *address, *port)

    err := http.ListenAndServe(completeAddress, nil)

    if err != nil {
        fmt.Println("\nERROR")
        fmt.Println(err);
    }
}
<commit_msg>allow listening on all interfaces, platform-independent modifications<commit_after>package main

import (
	"flag"
	"fmt"
	"net"
	"net/http"
	"os"
)

var (
	port *int = flag.Int("p", 8080, "port to listen for connections")
	// address may be empty to listen on all interfaces
	address   *string = flag.String("a", "", "address to bind to")
	directory string
)

func main() {
	flag.Parse()

	var err error
	directory := flag.Arg(0)

	if len(directory) == 0 {
		// No directory specified. Use current directory to host files
		// use os.Getwd() for platform-independent resolving of current working directory
		directory, err = os.Getwd()
		if err != nil {
			fmt.Printf("error retrieving current directory: %v", err)
			os.Exit(1)
		}
	}

	http.Handle("/", http.FileServer(http.Dir(directory)))

	completeAddress := net.JoinHostPort(*address, fmt.Sprint(*port))

	fmt.Printf("Starting http server at %s\n", completeAddress)

	fmt.Println("Hit CTRL-C to stop the server")

	err = http.ListenAndServe(completeAddress, nil)

	if err != nil {
		fmt.Println("\nERROR")
		fmt.Println(err)
	}
}
<|endoftext|>


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

package simplesession

import (
	"bytes"
	"crypto/rand"
	"crypto/sha1"
	"encoding/gob"
	"encoding/hex"
	"errors"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"sync"
	"time"
)

const (
	sidLength = 32
	sfileDir  = "/tmp"
)

func New(res http.ResponseWriter, option *Option) (*SimpleSession, error) {
	id, err := generateId()
	if err != nil {
		return nil, err
	}

	fpath := filepath.Join(sfileDir, "gosession_"+id)

	ss := &SimpleSession{
		name:   "GSESSIONID",
		id:     id,
		fpath:  fpath,
		option: option,
		data:   make(map[string]interface{}),
	}

	cookie := &http.Cookie{
		Name:     ss.name,
		Value:    ss.id,
		Path:     ss.option.Path,
		Domain:   ss.option.Domain,
		MaxAge:   ss.option.MaxAge,
		Secure:   ss.option.Secure,
		HttpOnly: ss.option.HttpOnly,
	}
	http.SetCookie(res, cookie)

	return ss, nil
}

type SimpleSession struct {
	name   string
	id     string
	fpath  string
	option *Option
	data   map[string]interface{}
}

func (ss *SimpleSession) Name() string {
	return ss.name
}

func (ss *SimpleSession) Id() string {
	return ss.id
}

func (ss *SimpleSession) FilePath() string {
	return ss.fpath
}

func (ss *SimpleSession) Set(key string, val interface{}) {
	ss.data[key] = val
}

func (ss *SimpleSession) Get(key string) interface{} {
	if val, ok := ss.data[key]; ok {
		return val
	}
	return nil
}

func (ss *SimpleSession) Del(key string) {
	if _, ok := ss.data[key]; ok {
		delete(ss.data, key)
	}
}

func (ss *SimpleSession) Write(res http.ResponseWriter, req *http.Request) error {
	serialized, err := serialize(ss.data)
	if err != nil {
		return err
	}

	var fmutex = &sync.RWMutex{}

	fmutex.Lock()
	fp, err := os.OpenFile(ss.fpath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
	if err != nil {
		return errors.New("simplesession: could not open session file")
	}

	if err = os.Chown(ss.fpath, os.Getuid(), os.Getgid()); err != nil {
		return errors.New("simplesession: could not change file owner")
	}
	defer fp.Close()

	if _, err = fp.Write(serialized); err != nil {
		return errors.New("simplesession: could not write session file")
	}
	defer fmutex.Unlock()

	return nil
}

// Read reads stored session
func Read(req *http.Request) (*SimpleSession, error) {
	cke, err := req.Cookie("GSESSIONID")
	if err != nil {
		return nil, errors.New("simplesession: session not set")
	}

	option := &Option{
		Path:     cke.Path,
		Domain:   cke.Domain,
		MaxAge:   cke.MaxAge,
		Secure:   cke.Secure,
		HttpOnly: cke.HttpOnly,
	}

	fpath := filepath.Join(sfileDir, "gosession_"+cke.Value)

	fp, err := os.OpenFile(fpath, os.O_RDONLY, 0400)
	if err != nil {
		return nil, errors.New("simplesession: could not open session file")
	}
	defer fp.Close()

	buf := make([]byte, 128)
	var serialized []byte
	for {
		_, err = fp.Read(buf)
		if err != nil || err == io.EOF {
			break
		}
		serialized = append(serialized, buf[0:]...)
	}

	data := make(map[string]interface{})
	if err = unserialize(serialized, data); err != nil {
		return nil, err
	}

	ss := &SimpleSession{
		name:   cke.Name,
		id:     cke.Value,
		fpath:  fpath,
		option: option,
		data:   data,
	}

	return ss, nil
}

func (ss *SimpleSession) Destroy(res http.ResponseWriter) error {

	cookie := &http.Cookie{
		Name:    ss.name,
		Value:   "",
		Path:    ss.option.Path,
		Domain:  ss.option.Domain,
		Expires: time.Unix(1, 0),
		//MaxAge:   -1,
		Secure:   false,
		HttpOnly: true,
	}
	http.SetCookie(res, cookie)

	if err := os.Remove(ss.fpath); err != nil {
		return errors.New("simplesession: no session file found")
	}

	return nil
}

// generateId generates a session id
func generateId() (string, error) {

	hash := sha1.New()
	io.WriteString(hash, strconv.FormatInt(time.Now().Unix(), 10))

	uran := make([]byte, 1024)
	if _, err := io.ReadFull(rand.Reader, uran); err != nil {
		return "", errors.New("simplesession: could not generate random key")
	}

	hash.Write(uran)
	id := hex.EncodeToString(hash.Sum(nil))

	fpath := filepath.Join(sfileDir, "gosession_"+id)
	if _, err := os.Stat(fpath); err == nil {
		generateId()
	}

	return id[0:sidLength], nil
}

// serialize encodes a value using binary.
func serialize(src map[string]interface{}) ([]byte, error) {
	buf := new(bytes.Buffer)
	if err := gob.NewEncoder(buf).Encode(src); err != nil {
		return nil, errors.New("simplesession: error occured while serializing session")
	}
	return buf.Bytes(), nil
}

// unserialize decodes a value using binary.
func unserialize(src []byte, dst map[string]interface{}) error {
	buf := bytes.NewReader(src)
	if err := gob.NewDecoder(buf).Decode(&dst); err != nil {
		return errors.New("simplesession: error occured while unserializing session")
	}
	return nil
}

type Option struct {
	Path     string
	Domain   string
	MaxAge   int
	Secure   bool
	HttpOnly bool
}
<commit_msg>Modified Write function<commit_after>//Copyright 2014 Mahendra Kathirvel. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package simplesession

import (
	"bytes"
	"crypto/rand"
	"crypto/sha1"
	"encoding/gob"
	"encoding/hex"
	"errors"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"sync"
	"time"
)

const (
	sidLength = 32
	sfileDir  = "/tmp"
)

func New(res http.ResponseWriter, option *Option) (*SimpleSession, error) {
	id, err := generateId()
	if err != nil {
		return nil, err
	}

	fpath := filepath.Join(sfileDir, "gosession_"+id)

	ss := &SimpleSession{
		name:   "GSESSIONID",
		id:     id,
		fpath:  fpath,
		option: option,
		data:   make(map[string]interface{}),
	}

	cookie := &http.Cookie{
		Name:   ss.name,
		Value:  ss.id,
		Path:   ss.option.Path,
		Domain: ss.option.Domain,
		//MaxAge:   ss.option.MaxAge,
		Secure:   ss.option.Secure,
		HttpOnly: ss.option.HttpOnly,
	}
	http.SetCookie(res, cookie)

	return ss, nil
}

type SimpleSession struct {
	name   string
	id     string
	fpath  string
	option *Option
	data   map[string]interface{}
}

func (ss *SimpleSession) Name() string {
	return ss.name
}

func (ss *SimpleSession) Id() string {
	return ss.id
}

func (ss *SimpleSession) FilePath() string {
	return ss.fpath
}

func (ss *SimpleSession) Set(key string, val interface{}) {
	ss.data[key] = val
}

func (ss *SimpleSession) Get(key string) interface{} {
	if val, ok := ss.data[key]; ok {
		return val
	}
	return nil
}

func (ss *SimpleSession) Del(key string) {
	if _, ok := ss.data[key]; ok {
		delete(ss.data, key)
	}
}

// Read reads stored session
func Read(req *http.Request) (*SimpleSession, error) {
	cke, err := req.Cookie("GSESSIONID")
	if err != nil {
		return nil, errors.New("simplesession: session not set")
	}

	option := &Option{
		Path:   cke.Path,
		Domain: cke.Domain,
		//MaxAge:   cke.MaxAge,
		Secure:   cke.Secure,
		HttpOnly: cke.HttpOnly,
	}

	fpath := filepath.Join(sfileDir, "gosession_"+cke.Value)

	fp, err := os.OpenFile(fpath, os.O_RDONLY, 0400)
	if err != nil {
		return nil, errors.New("simplesession: could not open session file")
	}
	defer fp.Close()

	var serialized []byte
	buf := make([]byte, 128)
	for {
		var n int
		n, err = fp.Read(buf)
		serialized = append(serialized, buf[0:n]...)
		if err != nil || err == io.EOF {
			break
		}
	}

	data := make(map[string]interface{})
	if err = unserialize(serialized, data); err != nil {
		return nil, err
	}

	ss := &SimpleSession{
		name:   cke.Name,
		id:     cke.Value,
		fpath:  fpath,
		option: option,
		data:   data,
	}

	return ss, nil
}

func (ss *SimpleSession) Write() error {
	serialized, err := serialize(ss.data)
	if err != nil {
		return err
	}

	var fmutex sync.RWMutex

	fmutex.Lock()
	fp, err := os.OpenFile(ss.fpath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
	if err != nil {
		return errors.New("simplesession: could not open session file")
	}

	if err = os.Chown(ss.fpath, os.Getuid(), os.Getgid()); err != nil {
		return errors.New("simplesession: could not change file owner")
	}
	defer fp.Close()

	if _, err = fp.Write(serialized); err != nil {
		return errors.New("simplesession: could not write session file")
	}
	defer fmutex.Unlock()

	return nil
}

func (ss *SimpleSession) Destroy(res http.ResponseWriter) error {

	cookie := &http.Cookie{
		Name:   ss.name,
		Value:  "",
		Path:   ss.option.Path,
		Domain: ss.option.Domain,
		//Expires: time.Unix(1, 0),
		MaxAge:   -1,
		Secure:   false,
		HttpOnly: true,
	}
	http.SetCookie(res, cookie)

	if err := os.Remove(ss.fpath); err != nil {
		return errors.New("simplesession: no session file found")
	}

	return nil
}

// generateId generates a session id
func generateId() (string, error) {

	hash := sha1.New()
	io.WriteString(hash, strconv.FormatInt(time.Now().Unix(), 10))

	uran := make([]byte, 1024)
	if _, err := io.ReadFull(rand.Reader, uran); err != nil {
		return "", errors.New("simplesession: could not generate random key")
	}

	hash.Write(uran)
	id := hex.EncodeToString(hash.Sum(nil))

	fpath := filepath.Join(sfileDir, "gosession_"+id)
	if _, err := os.Stat(fpath); err == nil {
		return generateId()
	} else {
		return id[0:sidLength], nil
	}
}

// serialize encodes a value using binary.
func serialize(src map[string]interface{}) ([]byte, error) {
	buf := new(bytes.Buffer)
	if err := gob.NewEncoder(buf).Encode(src); err != nil {
		return nil, errors.New("simplesession: error occured while serializing session")
	}
	return buf.Bytes(), nil
}

// unserialize decodes a value using binary.
func unserialize(src []byte, dst map[string]interface{}) error {
	buf := bytes.NewReader(src)
	if err := gob.NewDecoder(buf).Decode(&dst); err != nil {
		return errors.New("simplesession: error occured while unserializing session")
	}
	return nil
}

type Option struct {
	Path   string
	Domain string
	//MaxAge   int
	Secure   bool
	HttpOnly bool
}
<|endoftext|>


<commit_before>package geddit

import (
	"fmt"
	"github.com/toqueteos/webbrowser"
	"io/ioutil"
	"os"
	"path"
	"strings"
	"testing"
	"time"
)

func TestSubmit(t *testing.T) {

	session, err := NewLoginSession(
		"redditgolang",
		"apitest11",
		"tester",
	)
	if err != nil {
		t.Error(err)
	}

	subreddit, err := session.AboutSubreddit("mybottester")
	if err != nil {
		t.Error(err)
	}

	needsCaptcha, err := session.NeedsCaptcha()
	if err != nil {
		t.Error(err)
	}

	t.Log("Needs captcha:", needsCaptcha)

	if needsCaptcha {
		iden, err := session.NewCaptchaIden()
		if err != nil {
			t.Error(err)
		}

		stat, _ := os.Stdin.Stat()
		if (stat.Mode() & os.ModeCharDevice) == 0 {
			fmt.Println("data is being piped to stdin")
		} else {
			fmt.Println("stdin is from a terminal")
		}

		//_, err = session.CaptchaImage(iden)
		//if err != nil {
		//	t.Error(err)
		//}
		webbrowser.Open("http://www.reddit.com/captcha/" + iden)
		fmt.Println("Enter captcha in captcha.txt")

		ind0 := strings.Index(os.Args[0], "github.com")
		ind1 := strings.Index(os.Args[0], "/_test")
		p0 := os.Getenv("GOPATH")
		p0 = path.Join(p0, "src", os.Args[0][ind0:ind1], "captcha.txt")
		fmt.Println(p0)
		time.Sleep(time.Second * 7)
		for {
			if _, err = os.Stat(p0); err != nil {
				time.Sleep(time.Second * 3)
			} else {
				break
			}
		}
		bb, err := ioutil.ReadFile(p0)
		if err != nil {
			t.Error(err)
		}
		captcha := strings.TrimSpace(string(bb))
		fmt.Println("CAPTCHA:", captcha)
		os.Remove(p0)

		err = session.Submit(NewTextSubmission(subreddit.Name, "CAPTCHA TESTING TEXT", "TEST TEXT", true, &Captcha{iden, captcha}))
		if err != nil {
			t.Error(err)
		}

		// there is a submission delay!
		// needs to find a way to test both without the time delay, maybe two accounts
		//
		//err = session.Submit(NewLinkSubmission(subreddit.Name, "CAPTCHA TESTING LINK", "https://github.com/jzelinskie/reddit", true, &Captcha{iden, captcha}))
		//if err != nil {
		//	t.Error(err)
		//}

	} else {

		err = session.Submit(NewTextSubmission(subreddit.Name, "TESTING TEXT", "TEST TEXT", true, &Captcha{}))
		if err != nil {
			t.Error(err)
		}

		// there is a submission delay!
		// needs to find a way to test both without the time delay, maybe two accounts
		//
		//err = session.Submit(NewLinkSubmission(subreddit.Name, "TESTING LINK", "https://github.com/jzelinskie/reddit", true, &Captcha{}))
		//if err != nil {
		//	t.Error(err)
		//}
	}

}

func TestListings(t *testing.T) {
	session, err := NewLoginSession(
		"redditgolang",
		"apitest11",
		"tester",
	)
	if err != nil {
		t.Fatal(err)
	}

	_, err = session.MySaved(NewSubmissions, "")
	if err != nil {
		t.Fatal(err)
	}
}
<commit_msg>fixed url<commit_after>package geddit

import (
	"fmt"
	"github.com/toqueteos/webbrowser"
	"io/ioutil"
	"os"
	"path"
	"strings"
	"testing"
	"time"
)

func TestSubmit(t *testing.T) {

	session, err := NewLoginSession(
		"redditgolang",
		"apitest11",
		"tester",
	)
	if err != nil {
		t.Error(err)
	}

	subreddit, err := session.AboutSubreddit("mybottester")
	if err != nil {
		t.Error(err)
	}

	needsCaptcha, err := session.NeedsCaptcha()
	if err != nil {
		t.Error(err)
	}

	t.Log("Needs captcha:", needsCaptcha)

	if needsCaptcha {
		iden, err := session.NewCaptchaIden()
		if err != nil {
			t.Error(err)
		}

		stat, _ := os.Stdin.Stat()
		if (stat.Mode() & os.ModeCharDevice) == 0 {
			fmt.Println("data is being piped to stdin")
		} else {
			fmt.Println("stdin is from a terminal")
		}

		//_, err = session.CaptchaImage(iden)
		//if err != nil {
		//	t.Error(err)
		//}
		webbrowser.Open("http://www.reddit.com/captcha/" + iden)
		fmt.Println("Enter captcha in captcha.txt")

		ind0 := strings.Index(os.Args[0], "github.com")
		ind1 := strings.Index(os.Args[0], "/_test")
		p0 := os.Getenv("GOPATH")
		p0 = path.Join(p0, "src", os.Args[0][ind0:ind1], "captcha.txt")
		fmt.Println(p0)
		time.Sleep(time.Second * 7)
		for {
			if _, err = os.Stat(p0); err != nil {
				time.Sleep(time.Second * 3)
			} else {
				break
			}
		}
		bb, err := ioutil.ReadFile(p0)
		if err != nil {
			t.Error(err)
		}
		captcha := strings.TrimSpace(string(bb))
		fmt.Println("CAPTCHA:", captcha)
		os.Remove(p0)

		err = session.Submit(NewTextSubmission(subreddit.Name, "CAPTCHA TESTING TEXT", "TEST TEXT", true, &Captcha{iden, captcha}))
		if err != nil {
			t.Error(err)
		}

		// there is a submission delay!
		// needs to find a way to test both without the time delay, maybe two accounts
		//
		//err = session.Submit(NewLinkSubmission(subreddit.Name, "CAPTCHA TESTING LINK", "https://github.com/jzelinskie/geddit", true, &Captcha{iden, captcha}))
		//if err != nil {
		//	t.Error(err)
		//}

	} else {

		err = session.Submit(NewTextSubmission(subreddit.Name, "TESTING TEXT", "TEST TEXT", true, &Captcha{}))
		if err != nil {
			t.Error(err)
		}

		// there is a submission delay!
		// needs to find a way to test both without the time delay, maybe two accounts
		//
		//err = session.Submit(NewLinkSubmission(subreddit.Name, "TESTING LINK", "https://github.com/jzelinskie/geddit", true, &Captcha{}))
		//if err != nil {
		//	t.Error(err)
		//}
	}

}

func TestListings(t *testing.T) {
	session, err := NewLoginSession(
		"redditgolang",
		"apitest11",
		"tester",
	)
	if err != nil {
		t.Fatal(err)
	}

	_, err = session.MySaved(NewSubmissions, "")
	if err != nil {
		t.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package dockercli

import (
	"compress/bzip2"
	"compress/gzip"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"
	"syscall"

	"github.com/artyom/untar"
	"github.com/coreos/pkg/capnslog"
	"github.com/docker/distribution"
	"github.com/docker/distribution/manifest/schema1"
	"github.com/docker/docker/client"
	"github.com/docker/docker/image"
	"github.com/docker/docker/layer"
	"github.com/docker/docker/reference"
	"github.com/jgsqware/clairctl/config"
	"github.com/opencontainers/go-digest"
)

var log = capnslog.NewPackageLogger("github.com/jgsqware/clairctl", "dockercli")

//GetLocalManifest retrieve manifest for local image
func GetLocalManifest(imageName string, withExport bool) (reference.NamedTagged, distribution.Manifest, error) {

	n, err := reference.ParseNamed(imageName)
	if err != nil {
		return nil, nil, err
	}
	var image reference.NamedTagged
	if reference.IsNameOnly(n) {
		image = reference.WithDefaultTag(n).(reference.NamedTagged)
	} else {
		image = n.(reference.NamedTagged)
	}
	if err != nil {
		return nil, nil, err
	}
	var manifest distribution.Manifest
	if withExport {
		manifest, err = save(image.Name() + ":" + image.Tag())
	} else {
		manifest, err = historyFromCommand(image.Name() + ":" + image.Tag())
	}

	if err != nil {
		return nil, schema1.SignedManifest{}, err
	}
	m := manifest.(schema1.SignedManifest)
	m.Name = image.Name()
	m.Tag = image.Tag()
	return image, m, err
}

func saveImage(imageName string, fo *os.File) error {

	return nil
	// save.Stderr = &stderr

	// save.Stdout = writer
	// err := save.Run()
	// if err != nil {
	// 	return errors.New(stderr.String())
	// }

	// return nil
}

func save(imageName string) (distribution.Manifest, error) {
	path := config.TmpLocal() + "/" + strings.Split(imageName, ":")[0] + "/blobs"

	if _, err := os.Stat(path); os.IsExist(err) {
		err := os.RemoveAll(path)
		if err != nil {
			return nil, err
		}
	}

	err := os.MkdirAll(path, 0755)
	if err != nil {
		return nil, err
	}

	log.Debug("docker image to save: ", imageName)
	log.Debug("saving in: ", path)

	cli, err := client.NewEnvClient()
	if err != nil {
		panic(err)
	}

	img, err := cli.ImageSave(context.Background(), []string{imageName})
	if err != nil {
		return nil, fmt.Errorf("cannot save image %s: %s", imageName, err)
	}
	all, err := ioutil.ReadAll(img)
	if err != nil {
		panic(err)
	}
	img.Close()

	fo, err := os.Create(path + "/output.tar")
	// close fo on exit and check for its returned error
	defer func() {
		if err := fo.Close(); err != nil {
			panic(err)
		}
	}()

	if err != nil {
		log.Error(err)
		return nil, err
	}

	if _, err := fo.Write(all); err != nil {
		panic(err)
	}

	err = openAndUntar(path+"/output.tar", path)
	if err != nil {
		log.Error(err)
		return nil, err
	}

	err = os.Remove(path + "/output.tar")
	if err != nil {
		log.Error(err)
		return nil, err
	}
	return historyFromManifest(path)
}

func historyFromManifest(path string) (distribution.Manifest, error) {
	mf, err := os.Open(path + "/manifest.json")
	defer mf.Close()

	if err != nil {
		return schema1.SignedManifest{}, err
	}

	// https://github.com/docker/docker/blob/master/image/tarexport/tarexport.go#L17
	type manifestItem struct {
		Config       string
		RepoTags     []string
		Layers       []string
		Parent       image.ID                                 `json:",omitempty"`
		LayerSources map[layer.DiffID]distribution.Descriptor `json:",omitempty"`
	}

	var manifest []manifestItem
	if err = json.NewDecoder(mf).Decode(&manifest); err != nil {
		return schema1.SignedManifest{}, err
	} else if len(manifest) != 1 {
		return schema1.SignedManifest{}, err
	}
	var layers []string
	for _, layer := range manifest[0].Layers {
		layers = append(layers, strings.TrimSuffix(layer, "/layer.tar"))
	}
	var m schema1.SignedManifest

	for _, layer := range manifest[0].Layers {
		var d digest.Digest
		d, err := digest.Parse("sha256:" + strings.TrimSuffix(layer, "/layer.tar"))
		if err != nil {
			return schema1.SignedManifest{}, err
		}
		m.FSLayers = append(m.FSLayers, schema1.FSLayer{BlobSum: d})
	}

	return m, nil
}

func historyFromCommand(imageName string) (schema1.SignedManifest, error) {

	client, err := client.NewEnvClient()
	if err != nil {
		return schema1.SignedManifest{}, err
	}
	histories, err := client.ImageHistory(context.Background(), imageName)

	manifest := schema1.SignedManifest{}
	for _, history := range histories {
		var d digest.Digest
		d, err := digest.Parse(history.ID)
		if err != nil {
			return schema1.SignedManifest{}, err
		}
		manifest.FSLayers = append(manifest.FSLayers, schema1.FSLayer{BlobSum: d})
	}
	return manifest, nil
}

func openAndUntar(name, dst string) error {
	var rd io.Reader
	f, err := os.Open(name)
	defer f.Close()

	if err != nil {
		return err
	}
	rd = f
	if strings.HasSuffix(name, ".gz") || strings.HasSuffix(name, ".tgz") {
		gr, err := gzip.NewReader(f)
		if err != nil {
			return err
		}
		defer gr.Close()
		rd = gr
	} else if strings.HasSuffix(name, ".bz2") {
		rd = bzip2.NewReader(f)
	}
	if err := os.MkdirAll(dst, os.ModeDir|os.ModePerm); err != nil {
		return err
	}
	// resetting umask is essential to have exact permissions on unpacked
	// files; it's not not put inside untar function as it changes
	// process-wide umask
	mask := syscall.Umask(0)
	defer syscall.Umask(mask)
	return untar.Untar(rd, dst)
}
<commit_msg>Reduce memory usage. (#91)<commit_after>package dockercli

import (
	"compress/bzip2"
	"compress/gzip"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"strings"
	"syscall"

	"github.com/artyom/untar"
	"github.com/coreos/pkg/capnslog"
	"github.com/docker/distribution"
	"github.com/docker/distribution/manifest/schema1"
	"github.com/docker/docker/client"
	"github.com/docker/docker/image"
	"github.com/docker/docker/layer"
	"github.com/docker/docker/reference"
	"github.com/jgsqware/clairctl/config"
	"github.com/opencontainers/go-digest"
)

var log = capnslog.NewPackageLogger("github.com/jgsqware/clairctl", "dockercli")

//GetLocalManifest retrieve manifest for local image
func GetLocalManifest(imageName string, withExport bool) (reference.NamedTagged, distribution.Manifest, error) {

	n, err := reference.ParseNamed(imageName)
	if err != nil {
		return nil, nil, err
	}
	var image reference.NamedTagged
	if reference.IsNameOnly(n) {
		image = reference.WithDefaultTag(n).(reference.NamedTagged)
	} else {
		image = n.(reference.NamedTagged)
	}
	if err != nil {
		return nil, nil, err
	}
	var manifest distribution.Manifest
	if withExport {
		manifest, err = save(image.Name() + ":" + image.Tag())
	} else {
		manifest, err = historyFromCommand(image.Name() + ":" + image.Tag())
	}

	if err != nil {
		return nil, schema1.SignedManifest{}, err
	}
	m := manifest.(schema1.SignedManifest)
	m.Name = image.Name()
	m.Tag = image.Tag()
	return image, m, err
}

func saveImage(imageName string, fo *os.File) error {

	return nil
	// save.Stderr = &stderr

	// save.Stdout = writer
	// err := save.Run()
	// if err != nil {
	// 	return errors.New(stderr.String())
	// }

	// return nil
}

func save(imageName string) (distribution.Manifest, error) {
	path := config.TmpLocal() + "/" + strings.Split(imageName, ":")[0] + "/blobs"

	if _, err := os.Stat(path); os.IsExist(err) {
		err := os.RemoveAll(path)
		if err != nil {
			return nil, err
		}
	}

	err := os.MkdirAll(path, 0755)
	if err != nil {
		return nil, err
	}

	log.Debug("docker image to save: ", imageName)
	log.Debug("saving in: ", path)

	cli, err := client.NewEnvClient()
	if err != nil {
		panic(err)
	}

	img, err := cli.ImageSave(context.Background(), []string{imageName})
	if err != nil {
		return nil, fmt.Errorf("cannot save image %s: %s", imageName, err)
	}
	defer img.Close()

	fo, err := os.Create(path + "/output.tar")
	// close fo on exit and check for its returned error
	defer func() {
		if err := fo.Close(); err != nil {
			panic(err)
		}
	}()

	if err != nil {
		log.Error(err)
		return nil, err
	}

	// make a buffer to keep chunks that are read
	buf := make([]byte, 1024)
	for {
		// read a chunk
		n, err := img.Read(buf)
		if err != nil && err != io.EOF {
			log.Error(err)
			return nil, err
		}
		if n == 0 {
			break
		}

		// write a chunk
		if _, err := fo.Write(buf[:n]); err != nil {
			log.Error(err)
			return nil, err
		}
	}

	err = openAndUntar(path+"/output.tar", path)
	if err != nil {
		log.Error(err)
		return nil, err
	}

	err = os.Remove(path + "/output.tar")
	if err != nil {
		log.Error(err)
		return nil, err
	}
	return historyFromManifest(path)
}

func historyFromManifest(path string) (distribution.Manifest, error) {
	mf, err := os.Open(path + "/manifest.json")
	defer mf.Close()

	if err != nil {
		return schema1.SignedManifest{}, err
	}

	// https://github.com/docker/docker/blob/master/image/tarexport/tarexport.go#L17
	type manifestItem struct {
		Config       string
		RepoTags     []string
		Layers       []string
		Parent       image.ID                                 `json:",omitempty"`
		LayerSources map[layer.DiffID]distribution.Descriptor `json:",omitempty"`
	}

	var manifest []manifestItem
	if err = json.NewDecoder(mf).Decode(&manifest); err != nil {
		return schema1.SignedManifest{}, err
	} else if len(manifest) != 1 {
		return schema1.SignedManifest{}, err
	}
	var layers []string
	for _, layer := range manifest[0].Layers {
		layers = append(layers, strings.TrimSuffix(layer, "/layer.tar"))
	}
	var m schema1.SignedManifest

	for _, layer := range manifest[0].Layers {
		var d digest.Digest
		d, err := digest.Parse("sha256:" + strings.TrimSuffix(layer, "/layer.tar"))
		if err != nil {
			return schema1.SignedManifest{}, err
		}
		m.FSLayers = append(m.FSLayers, schema1.FSLayer{BlobSum: d})
	}

	return m, nil
}

func historyFromCommand(imageName string) (schema1.SignedManifest, error) {

	client, err := client.NewEnvClient()
	if err != nil {
		return schema1.SignedManifest{}, err
	}
	histories, err := client.ImageHistory(context.Background(), imageName)

	manifest := schema1.SignedManifest{}
	for _, history := range histories {
		var d digest.Digest
		d, err := digest.Parse(history.ID)
		if err != nil {
			return schema1.SignedManifest{}, err
		}
		manifest.FSLayers = append(manifest.FSLayers, schema1.FSLayer{BlobSum: d})
	}
	return manifest, nil
}

func openAndUntar(name, dst string) error {
	var rd io.Reader
	f, err := os.Open(name)
	defer f.Close()

	if err != nil {
		return err
	}
	rd = f
	if strings.HasSuffix(name, ".gz") || strings.HasSuffix(name, ".tgz") {
		gr, err := gzip.NewReader(f)
		if err != nil {
			return err
		}
		defer gr.Close()
		rd = gr
	} else if strings.HasSuffix(name, ".bz2") {
		rd = bzip2.NewReader(f)
	}
	if err := os.MkdirAll(dst, os.ModeDir|os.ModePerm); err != nil {
		return err
	}
	// resetting umask is essential to have exact permissions on unpacked
	// files; it's not not put inside untar function as it changes
	// process-wide umask
	mask := syscall.Umask(0)
	defer syscall.Umask(mask)
	return untar.Untar(rd, dst)
}
<|endoftext|>


<commit_before>
//----------------------------------------
//
// Copyright © ying32. All Rights Reserved.
//
// Licensed under Apache License 2.0
//
//----------------------------------------

package colors

const (
	// Delphi中的颜色表，Delphi中的TColor与一般HTML的RGB有点不一样，反过来的
	// 以BGR表示.

	ClClSysNone  = 0x1FFFFFFF
	ClSysDefault = 0x20000000
	// Actual colors
	ClAliceblue            = 0xFFF8F0
	ClAntiquewhite         = 0xD7EBFA
	ClAqua                 = 0xFFFF00
	ClAquamarine           = 0xD4FF7F
	ClAzure                = 0xFFFFF0
	ClBeige                = 0xDCF5F5
	ClBisque               = 0xC4E4FF
	ClBlack                = 0x000000
	ClBlanchedalmond       = 0xCDEBFF
	ClBlue                 = 0xFF0000
	ClBlueviolet           = 0xE22B8A
	ClBrown                = 0x2A2AA5
	ClBurlywood            = 0x87B8DE
	ClCadetblue            = 0xA09E5F
	ClChartreuse           = 0x00FF7F
	ClChocolate            = 0x1E69D2
	ClCoral                = 0x507FFF
	ClCornflowerblue       = 0xED9564
	ClCornsilk             = 0xDCF8FF
	ClCrimson              = 0x3C14DC
	ClCyan                 = 0xFFFF00
	ClDarkblue             = 0x8B0000
	ClDarkcyan             = 0x8B8B00
	ClDarkgoldenrod        = 0x0B86B8
	ClDarkgray             = 0xA9A9A9
	ClDarkgreen            = 0x006400
	ClDarkgrey             = 0xA9A9A9
	ClDarkkhaki            = 0x6BB7BD
	ClDarkmagenta          = 0x8B008B
	ClDarkolivegreen       = 0x2F6B55
	ClDarkorange           = 0x008CFF
	ClDarkorchid           = 0xCC3299
	ClDarkred              = 0x00008B
	ClDarksalmon           = 0x7A96E9
	ClDarkseagreen         = 0x8FBC8F
	ClDarkslateblue        = 0x8B3D48
	ClDarkslategray        = 0x4F4F2F
	ClDarkslategrey        = 0x4F4F2F
	ClDarkturquoise        = 0xD1CE00
	ClDarkviolet           = 0xD30094
	ClDeeppink             = 0x9314FF
	ClDeepskyblue          = 0xFFBF00
	ClDimgray              = 0x696969
	ClDimgrey              = 0x696969
	ClDodgerblue           = 0xFF901E
	ClFirebrick            = 0x2222B2
	ClFloralwhite          = 0xF0FAFF
	ClForestgreen          = 0x228B22
	ClFuchsia              = 0xFF00FF
	ClGainsboro            = 0xDCDCDC
	ClGhostwhite           = 0xFFF8F8
	ClGold                 = 0x00D7FF
	ClGoldenrod            = 0x20A5DA
	ClGray                 = 0x808080
	ClGreen                = 0x008000
	ClGreenyellow          = 0x2FFFAD
	ClGrey                 = 0x808080
	ClHoneydew             = 0xF0FFF0
	ClHotpink              = 0xB469FF
	ClIndianred            = 0x5C5CCD
	ClIndigo               = 0x82004B
	ClIvory                = 0xF0FFFF
	ClKhaki                = 0x8CE6F0
	ClLavender             = 0xFAE6E6
	ClLavenderblush        = 0xF5F0FF
	ClLawngreen            = 0x00FC7C
	ClLemonchiffon         = 0xCDFAFF
	ClLightblue            = 0xE6D8AD
	ClLightcoral           = 0x8080F0
	ClLightcyan            = 0xFFFFE0
	ClLightgoldenrodyellow = 0xD2FAFA
	ClLightgray            = 0xD3D3D3
	ClLightgreen           = 0x90EE90
	ClLightgrey            = 0xD3D3D3
	ClLightpink            = 0xC1B6FF
	ClLightsalmon          = 0x7AA0FF
	ClLightseagreen        = 0xAAB220
	ClLightskyblue         = 0xFACE87
	ClLightslategray       = 0x998877
	ClLightslategrey       = 0x998877
	ClLightsteelblue       = 0xDEC4B0
	ClLightyellow          = 0xE0FFFF
	ClLtGray               = 0xC0C0C0
	ClMedGray              = 0xA4A0A0
	ClDkGray               = 0x808080
	ClMoneyGreen           = 0xC0DCC0
	ClLegacySkyBlue        = 0xF0CAA6
	ClCream                = 0xF0FBFF
	ClLime                 = 0x00FF00
	ClLimegreen            = 0x32CD32
	ClLinen                = 0xE6F0FA
	ClMagenta              = 0xFF00FF
	ClMaroon               = 0x000080
	ClMediumaquamarine     = 0xAACD66
	ClMediumblue           = 0xCD0000
	ClMediumorchid         = 0xD355BA
	ClMediumpurple         = 0xDB7093
	ClMediumseagreen       = 0x71B33C
	ClMediumslateblue      = 0xEE687B
	ClMediumspringgreen    = 0x9AFA00
	ClMediumturquoise      = 0xCCD148
	ClMediumvioletred      = 0x8515C7
	ClMidnightblue         = 0x701919
	ClMintcream            = 0xFAFFF5
	ClMistyrose            = 0xE1E4FF
	ClMoccasin             = 0xB5E4FF
	ClNavajowhite          = 0xADDEFF
	ClNavy                 = 0x800000
	ClOldlace              = 0xE6F5FD
	ClOlive                = 0x008080
	ClOlivedrab            = 0x238E6B
	ClOrange               = 0x00A5FF
	ClOrangered            = 0x0045FF
	ClOrchid               = 0xD670DA
	ClPalegoldenrod        = 0xAAE8EE
	ClPalegreen            = 0x98FB98
	ClPaleturquoise        = 0xEEEEAF
	ClPalevioletred        = 0x9370DB
	ClPapayawhip           = 0xD5EFFF
	ClPeachpuff            = 0xB9DAFF
	ClPeru                 = 0x3F85CD
	ClPink                 = 0xCBC0FF
	ClPlum                 = 0xDDA0DD
	ClPowderblue           = 0xE6E0B0
	ClPurple               = 0x800080
	ClRed                  = 0x0000FF
	ClRosybrown            = 0x8F8FBC
	ClRoyalblue            = 0xE16941
	ClSaddlebrown          = 0x13458B
	ClSalmon               = 0x7280FA
	ClSandybrown           = 0x60A4F4
	ClSeagreen             = 0x578B2E
	ClSeashell             = 0xEEF5FF
	ClSienna               = 0x2D52A0
	ClSilver               = 0xC0C0C0
	ClSkyblue              = 0xEBCE87
	ClSlateblue            = 0xCD5A6A
	ClSlategray            = 0x908070
	ClSlategrey            = 0x908070
	ClSnow                 = 0xFAFAFF
	ClSpringgreen          = 0x7FFF00
	ClSteelblue            = 0xB48246
	ClTan                  = 0x8CB4D2
	ClTeal                 = 0x808000
	ClThistle              = 0xD8BFD8
	ClTomato               = 0x4763FF
	ClTurquoise            = 0xD0E040
	ClViolet               = 0xEE82EE
	ClWheat                = 0xB3DEF5
	ClWhite                = 0xFFFFFF
	ClWhitesmoke           = 0xF5F5F5
	ClYellow               = 0x00FFFF
	ClYellowgreen          = 0x32CD9A
	ClBtnFace              = 0xFF00000F
	ClNull                 = 0x00000000
)
<commit_msg>Then `colors` package adds `RGB` and `RGBToBGR` functions.<commit_after>//----------------------------------------
//
// Copyright © ying32. All Rights Reserved.
//
// Licensed under Apache License 2.0
//
//----------------------------------------

package colors

const (
	// Delphi中的颜色表，Delphi中的TColor与一般HTML的RGB有点不一样，反过来的
	// 以BGR表示.

	ClClSysNone  = 0x1FFFFFFF
	ClSysDefault = 0x20000000
	// Actual colors
	ClAliceblue            = 0xFFF8F0
	ClAntiquewhite         = 0xD7EBFA
	ClAqua                 = 0xFFFF00
	ClAquamarine           = 0xD4FF7F
	ClAzure                = 0xFFFFF0
	ClBeige                = 0xDCF5F5
	ClBisque               = 0xC4E4FF
	ClBlack                = 0x000000
	ClBlanchedalmond       = 0xCDEBFF
	ClBlue                 = 0xFF0000
	ClBlueviolet           = 0xE22B8A
	ClBrown                = 0x2A2AA5
	ClBurlywood            = 0x87B8DE
	ClCadetblue            = 0xA09E5F
	ClChartreuse           = 0x00FF7F
	ClChocolate            = 0x1E69D2
	ClCoral                = 0x507FFF
	ClCornflowerblue       = 0xED9564
	ClCornsilk             = 0xDCF8FF
	ClCrimson              = 0x3C14DC
	ClCyan                 = 0xFFFF00
	ClDarkblue             = 0x8B0000
	ClDarkcyan             = 0x8B8B00
	ClDarkgoldenrod        = 0x0B86B8
	ClDarkgray             = 0xA9A9A9
	ClDarkgreen            = 0x006400
	ClDarkgrey             = 0xA9A9A9
	ClDarkkhaki            = 0x6BB7BD
	ClDarkmagenta          = 0x8B008B
	ClDarkolivegreen       = 0x2F6B55
	ClDarkorange           = 0x008CFF
	ClDarkorchid           = 0xCC3299
	ClDarkred              = 0x00008B
	ClDarksalmon           = 0x7A96E9
	ClDarkseagreen         = 0x8FBC8F
	ClDarkslateblue        = 0x8B3D48
	ClDarkslategray        = 0x4F4F2F
	ClDarkslategrey        = 0x4F4F2F
	ClDarkturquoise        = 0xD1CE00
	ClDarkviolet           = 0xD30094
	ClDeeppink             = 0x9314FF
	ClDeepskyblue          = 0xFFBF00
	ClDimgray              = 0x696969
	ClDimgrey              = 0x696969
	ClDodgerblue           = 0xFF901E
	ClFirebrick            = 0x2222B2
	ClFloralwhite          = 0xF0FAFF
	ClForestgreen          = 0x228B22
	ClFuchsia              = 0xFF00FF
	ClGainsboro            = 0xDCDCDC
	ClGhostwhite           = 0xFFF8F8
	ClGold                 = 0x00D7FF
	ClGoldenrod            = 0x20A5DA
	ClGray                 = 0x808080
	ClGreen                = 0x008000
	ClGreenyellow          = 0x2FFFAD
	ClGrey                 = 0x808080
	ClHoneydew             = 0xF0FFF0
	ClHotpink              = 0xB469FF
	ClIndianred            = 0x5C5CCD
	ClIndigo               = 0x82004B
	ClIvory                = 0xF0FFFF
	ClKhaki                = 0x8CE6F0
	ClLavender             = 0xFAE6E6
	ClLavenderblush        = 0xF5F0FF
	ClLawngreen            = 0x00FC7C
	ClLemonchiffon         = 0xCDFAFF
	ClLightblue            = 0xE6D8AD
	ClLightcoral           = 0x8080F0
	ClLightcyan            = 0xFFFFE0
	ClLightgoldenrodyellow = 0xD2FAFA
	ClLightgray            = 0xD3D3D3
	ClLightgreen           = 0x90EE90
	ClLightgrey            = 0xD3D3D3
	ClLightpink            = 0xC1B6FF
	ClLightsalmon          = 0x7AA0FF
	ClLightseagreen        = 0xAAB220
	ClLightskyblue         = 0xFACE87
	ClLightslategray       = 0x998877
	ClLightslategrey       = 0x998877
	ClLightsteelblue       = 0xDEC4B0
	ClLightyellow          = 0xE0FFFF
	ClLtGray               = 0xC0C0C0
	ClMedGray              = 0xA4A0A0
	ClDkGray               = 0x808080
	ClMoneyGreen           = 0xC0DCC0
	ClLegacySkyBlue        = 0xF0CAA6
	ClCream                = 0xF0FBFF
	ClLime                 = 0x00FF00
	ClLimegreen            = 0x32CD32
	ClLinen                = 0xE6F0FA
	ClMagenta              = 0xFF00FF
	ClMaroon               = 0x000080
	ClMediumaquamarine     = 0xAACD66
	ClMediumblue           = 0xCD0000
	ClMediumorchid         = 0xD355BA
	ClMediumpurple         = 0xDB7093
	ClMediumseagreen       = 0x71B33C
	ClMediumslateblue      = 0xEE687B
	ClMediumspringgreen    = 0x9AFA00
	ClMediumturquoise      = 0xCCD148
	ClMediumvioletred      = 0x8515C7
	ClMidnightblue         = 0x701919
	ClMintcream            = 0xFAFFF5
	ClMistyrose            = 0xE1E4FF
	ClMoccasin             = 0xB5E4FF
	ClNavajowhite          = 0xADDEFF
	ClNavy                 = 0x800000
	ClOldlace              = 0xE6F5FD
	ClOlive                = 0x008080
	ClOlivedrab            = 0x238E6B
	ClOrange               = 0x00A5FF
	ClOrangered            = 0x0045FF
	ClOrchid               = 0xD670DA
	ClPalegoldenrod        = 0xAAE8EE
	ClPalegreen            = 0x98FB98
	ClPaleturquoise        = 0xEEEEAF
	ClPalevioletred        = 0x9370DB
	ClPapayawhip           = 0xD5EFFF
	ClPeachpuff            = 0xB9DAFF
	ClPeru                 = 0x3F85CD
	ClPink                 = 0xCBC0FF
	ClPlum                 = 0xDDA0DD
	ClPowderblue           = 0xE6E0B0
	ClPurple               = 0x800080
	ClRed                  = 0x0000FF
	ClRosybrown            = 0x8F8FBC
	ClRoyalblue            = 0xE16941
	ClSaddlebrown          = 0x13458B
	ClSalmon               = 0x7280FA
	ClSandybrown           = 0x60A4F4
	ClSeagreen             = 0x578B2E
	ClSeashell             = 0xEEF5FF
	ClSienna               = 0x2D52A0
	ClSilver               = 0xC0C0C0
	ClSkyblue              = 0xEBCE87
	ClSlateblue            = 0xCD5A6A
	ClSlategray            = 0x908070
	ClSlategrey            = 0x908070
	ClSnow                 = 0xFAFAFF
	ClSpringgreen          = 0x7FFF00
	ClSteelblue            = 0xB48246
	ClTan                  = 0x8CB4D2
	ClTeal                 = 0x808000
	ClThistle              = 0xD8BFD8
	ClTomato               = 0x4763FF
	ClTurquoise            = 0xD0E040
	ClViolet               = 0xEE82EE
	ClWheat                = 0xB3DEF5
	ClWhite                = 0xFFFFFF
	ClWhitesmoke           = 0xF5F5F5
	ClYellow               = 0x00FFFF
	ClYellowgreen          = 0x32CD9A
	ClBtnFace              = 0xFF00000F
	ClNull                 = 0x00000000
)

// RGB
func RGB(r, g, b byte) uint32 {
	return uint32(r) | (uint32(g) << 8) | (uint32(b) << 16)
}

// RGBToBGR
func RGBToBGR(rgb uint32) uint32 {
	return uint32(byte(rgb>>16)) | (uint32(byte(rgb>>8)) << 8) | (uint32(byte(rgb)) << 16)
}
<|endoftext|>


<commit_before>package sudoku

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
)

type Availables struct {
	Size    int
	Numbers map[int]bool
}

type Sudoku struct {
	Debug      bool
	Size       int
	SquareSize int
	Grid       [][]int
	Availables [][]Availables
	Groups     Groups
}
type Groups []Group

type Position struct {
	Y int
	X int
}

type Group struct {
	Size      int
	Positions []Position
}

func NewSudoku() Sudoku {
	return NewSudokuWithSize(3)
}

func NewAvailables(size int) Availables {
	availables := Availables{
		Size:    size,
		Numbers: make(map[int]bool, size),
	}
	for i := 1; i <= size; i++ {
		availables.Numbers[i] = true
	}
	return availables
}

func NewSudokuWithSize(sqsize int) Sudoku {
	size := sqsize * sqsize
	sudoku := Sudoku{
		Debug:      false,
		SquareSize: sqsize,
		Size:       size,
		Grid:       make([][]int, size),
		Availables: make([][]Availables, size),
		Groups:     make([]Group, 0),
	}
	for i := 0; i < size; i++ {
		sudoku.Grid[i] = make([]int, size)
		sudoku.Availables[i] = make([]Availables, size)
		for j := 0; j < size; j++ {
			sudoku.Availables[i][j] = NewAvailables(size)
		}
	}

	// horizontal groups
	for y := 0; y < size; y++ {
		group := Group{}
		for x := 0; x < size; x++ {
			group.Positions = append(group.Positions, Position{y, x})
		}
		sudoku.Groups = append(sudoku.Groups, group)
	}

	// vertical groups
	for x := 0; x < size; x++ {
		group := Group{}
		for y := 0; y < size; y++ {
			group.Positions = append(group.Positions, Position{y, x})
		}
		sudoku.Groups = append(sudoku.Groups, group)
	}
	// zone groups
	for a := 0; a < sudoku.SquareSize; a++ {
		for b := 0; b < sudoku.SquareSize; b++ {
			group := Group{}
			for c := 0; c < sudoku.SquareSize; c++ {
				for d := 0; d < sudoku.SquareSize; d++ {
					y := a*sudoku.SquareSize + c
					x := b*sudoku.SquareSize + d
					group.Positions = append(group.Positions, Position{y, x})
				}
			}
			sudoku.Groups = append(sudoku.Groups, group)
		}
	}

	return sudoku
}

func (a *Availables) String() string {
	output := ""
	for i := 1; i <= a.Size; i++ {
		if a.Numbers[i] {
			output += strconv.Itoa(i)
		} else {
			output += "."
		}
	}
	return output
}

func (a *Availables) SetNumber(number int) {
	for i := 1; i <= a.Size; i++ {
		a.Numbers[i] = i == number
	}
}

func (a *Availables) Length() int {
	length := 0
	for i := 1; i <= a.Size; i++ {
		if a.Numbers[i] {
			length++
		}
	}
	return length
}

func (a *Availables) Availables() []int {
	availables := []int{}
	for i := 1; i <= a.Size; i++ {
		if a.Numbers[i] {
			availables = append(availables, i)
		}
	}
	return availables
}

func (a *Availables) RemoveNumber(number int) bool {
	changed := false
	if a.Numbers[number] {
		changed = true
	}
	a.Numbers[number] = false
	return changed
}

func (g *Groups) MatchCoords(y, x int) []Group {
	groups := []Group{}
	for _, group := range *g {
		for _, pos := range group.Positions {
			if pos.Y == y && pos.X == x {
				groups = append(groups, group)
				break
			}
		}
	}
	return groups
}

func (s *Sudoku) SetNumber(y, x, number int) {
	s.Grid[y][x] = number
	s.Availables[y][x].SetNumber(number)
	for _, group := range s.Groups.MatchCoords(y, x) {
		for _, pos := range group.Positions {
			s.Availables[pos.Y][pos.X].RemoveNumber(number)
		}
	}
}

func (s *Sudoku) ParseString(input string) error {
	input = strings.TrimSpace(input)
	lines := strings.Split(input, "\n")
	for y, line := range lines[1 : s.Size+1] {
		for x := 0; x < s.Size; x++ {
			col := line[1+x*2 : 1+x*2+1]
			if col != " " {
				colNb, err := strconv.Atoi(col)
				if err != nil {
					return err
				}
				s.SetNumber(y, x, colNb)
			}
		}
	}
	return nil
}

func (s *Sudoku) String() string {
	lines := []string{}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*2-1)))

	for _, gridLine := range s.Grid {
		line := []string{}
		for _, col := range gridLine {
			line = append(line, strconv.Itoa(col))
		}
		lineStr := fmt.Sprintf("|%s|", strings.Join(line, " "))
		lineStr = strings.Replace(lineStr, "0", " ", -1)
		lines = append(lines, lineStr)
	}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*2-1)))
	return strings.Join(lines, "\n")
}

func (s *Sudoku) AvailablesString() string {
	lines := []string{}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*(s.Size+1)-1)))
	for _, availablesLine := range s.Availables {
		line := []string{}
		for _, availables := range availablesLine {
			line = append(line, availables.String())
		}
		lineStr := fmt.Sprintf("|%s|", strings.Join(line, " "))
		lines = append(lines, lineStr)
	}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*(s.Size+1)-1)))
	return strings.Join(lines, "\n")
}

func (s *Sudoku) Missings() int {
	missings := 0
	for _, line := range s.Grid {
		for _, col := range line {
			if col == 0 {
				missings++
			}
		}
	}
	return missings
}

func (s *Sudoku) ResolveOnlyOne() int {
	changed := 0
	for y := 0; y < s.Size; y++ {
		for x := 0; x < s.Size; x++ {
			if s.Grid[y][x] != 0 {
				continue
			}
			if s.Availables[y][x].Length() == 1 {
				s.SetNumber(y, x, s.Availables[y][x].Availables()[0])
				changed++
			}
		}
	}
	return changed
}

func (s *Sudoku) RemoveNumbersThatCanOnlyBeInFewPositions() int {
	changes := 0
	for _, group := range s.Groups {
		for idxA, posA := range group.Positions {
			identicalSlots := 0
			availableA := s.Availables[posA.Y][posA.X]
			if len(availableA.Availables()) != 2 {
				continue
			}
			for idxB, posB := range group.Positions {
				if idxA == idxB {
					continue
				}
				availableB := s.Availables[posB.Y][posB.X]
				if availableA.String() == availableB.String() {
					identicalSlots++
				}
			}
			if identicalSlots != 1 {
				continue
			}
			for idxB, posB := range group.Positions {
				if idxA == idxB {
					continue
				}
				availableB := s.Availables[posB.Y][posB.X]
				if availableA.String() != availableB.String() {
					for _, number := range availableA.Availables() {
						if availableB.RemoveNumber(number) {
							changes++
						}
					}
				}
			}
		}
	}
	return changes
}

func (s *Sudoku) Resolve() error {
	changes := 0
	iteration := 0
	kind := "start"

start:
	if s.Debug {
		logrus.Infof("#######  iteration=%-3d changes=%-2d kind=%s\n%s\n%s", iteration, changes, kind, s.String(), s.AvailablesString())
		logrus.Infof(strings.Repeat("#", 42))
	}
	iteration++

	kind = "resolve-only-one"
	if changes = s.ResolveOnlyOne(); changes > 0 {
		goto start
	}

	kind = "resolve-numbers-that-can-only-be-in-few-positions"
	if changes = s.RemoveNumbersThatCanOnlyBeInFewPositions(); changes > 0 {
		goto start
	}

	return nil
}
<commit_msg>Handling multiple sizes<commit_after>package sudoku

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
)

type Availables struct {
	Size    int
	Numbers map[int]bool
}

type Sudoku struct {
	Debug      bool
	Size       int
	SquareSize int
	Grid       [][]int
	Availables [][]Availables
	Groups     Groups
}
type Groups []Group

type Position struct {
	Y int
	X int
}

type Group struct {
	Size      int
	Positions []Position
}

func NewSudoku() Sudoku {
	return NewSudokuWithSize(3)
}

func NewAvailables(size int) Availables {
	availables := Availables{
		Size:    size,
		Numbers: make(map[int]bool, size),
	}
	for i := 1; i <= size; i++ {
		availables.Numbers[i] = true
	}
	return availables
}

func NewSudokuWithSize(sqsize int) Sudoku {
	size := sqsize * sqsize
	sudoku := Sudoku{
		Debug:      false,
		SquareSize: sqsize,
		Size:       size,
		Grid:       make([][]int, size),
		Availables: make([][]Availables, size),
		Groups:     make([]Group, 0),
	}
	for i := 0; i < size; i++ {
		sudoku.Grid[i] = make([]int, size)
		sudoku.Availables[i] = make([]Availables, size)
		for j := 0; j < size; j++ {
			sudoku.Availables[i][j] = NewAvailables(size)
		}
	}

	// horizontal groups
	for y := 0; y < size; y++ {
		group := Group{}
		for x := 0; x < size; x++ {
			group.Positions = append(group.Positions, Position{y, x})
		}
		sudoku.Groups = append(sudoku.Groups, group)
	}

	// vertical groups
	for x := 0; x < size; x++ {
		group := Group{}
		for y := 0; y < size; y++ {
			group.Positions = append(group.Positions, Position{y, x})
		}
		sudoku.Groups = append(sudoku.Groups, group)
	}
	// zone groups
	for a := 0; a < sudoku.SquareSize; a++ {
		for b := 0; b < sudoku.SquareSize; b++ {
			group := Group{}
			for c := 0; c < sudoku.SquareSize; c++ {
				for d := 0; d < sudoku.SquareSize; d++ {
					y := a*sudoku.SquareSize + c
					x := b*sudoku.SquareSize + d
					group.Positions = append(group.Positions, Position{y, x})
				}
			}
			sudoku.Groups = append(sudoku.Groups, group)
		}
	}

	return sudoku
}

func (a *Availables) String() string {
	output := ""
	for i := 1; i <= a.Size; i++ {
		if a.Numbers[i] {
			output += strconv.Itoa(i)
		} else {
			output += "."
		}
	}
	return output
}

func (a *Availables) SetNumber(number int) {
	for i := 1; i <= a.Size; i++ {
		a.Numbers[i] = i == number
	}
}

func (a *Availables) Length() int {
	length := 0
	for i := 1; i <= a.Size; i++ {
		if a.Numbers[i] {
			length++
		}
	}
	return length
}

func (a *Availables) Availables() []int {
	availables := []int{}
	for i := 1; i <= a.Size; i++ {
		if a.Numbers[i] {
			availables = append(availables, i)
		}
	}
	return availables
}

func (a *Availables) RemoveNumber(number int) bool {
	changed := false
	if a.Numbers[number] {
		changed = true
	}
	a.Numbers[number] = false
	return changed
}

func (g *Groups) MatchCoords(y, x int) []Group {
	groups := []Group{}
	for _, group := range *g {
		for _, pos := range group.Positions {
			if pos.Y == y && pos.X == x {
				groups = append(groups, group)
				break
			}
		}
	}
	return groups
}

func (s *Sudoku) SetNumber(y, x, number int) {
	s.Grid[y][x] = number
	s.Availables[y][x].SetNumber(number)
	for _, group := range s.Groups.MatchCoords(y, x) {
		for _, pos := range group.Positions {
			s.Availables[pos.Y][pos.X].RemoveNumber(number)
		}
	}
}

func (s *Sudoku) ParseString(input string) error {
	input = strings.TrimSpace(input)
	lines := strings.Split(input, "\n")
	for y, line := range lines[1 : s.Size+1] {
		for x := 0; x < s.Size; x++ {
			col := line[1+x*2 : 1+x*2+1]
			if col != " " {
				colNb, err := strconv.Atoi(col)
				if err != nil {
					return err
				}
				s.SetNumber(y, x, colNb)
			}
		}
	}
	return nil
}

func (s *Sudoku) String() string {
	lines := []string{}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*2-1)))

	for _, gridLine := range s.Grid {
		line := []string{}
		for _, col := range gridLine {
			line = append(line, strconv.Itoa(col))
		}
		lineStr := fmt.Sprintf("|%s|", strings.Join(line, " "))
		lineStr = strings.Replace(lineStr, "0", " ", -1)
		lines = append(lines, lineStr)
	}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*2-1)))
	return strings.Join(lines, "\n")
}

func (s *Sudoku) AvailablesString() string {
	lines := []string{}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*(s.Size+1)-1)))
	for _, availablesLine := range s.Availables {
		line := []string{}
		for _, availables := range availablesLine {
			line = append(line, availables.String())
		}
		lineStr := fmt.Sprintf("|%s|", strings.Join(line, " "))
		lines = append(lines, lineStr)
	}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*(s.Size+1)-1)))
	return strings.Join(lines, "\n")
}

func (s *Sudoku) Missings() int {
	missings := 0
	for _, line := range s.Grid {
		for _, col := range line {
			if col == 0 {
				missings++
			}
		}
	}
	return missings
}

func (s *Sudoku) ResolveOnlyOne() int {
	changed := 0
	for y := 0; y < s.Size; y++ {
		for x := 0; x < s.Size; x++ {
			if s.Grid[y][x] != 0 {
				continue
			}
			if s.Availables[y][x].Length() == 1 {
				s.SetNumber(y, x, s.Availables[y][x].Availables()[0])
				changed++
			}
		}
	}
	return changed
}

func (s *Sudoku) RemoveNumbersThatCanOnlyBeInFewPositions() int {
	changes := 0
	for _, group := range s.Groups {
		for idxA, posA := range group.Positions {
			identicalSlots := 0
			availableA := s.Availables[posA.Y][posA.X]
			for idxB, posB := range group.Positions {
				if idxA == idxB {
					continue
				}
				availableB := s.Availables[posB.Y][posB.X]
				if availableA.String() == availableB.String() {
					identicalSlots++
				}
			}
			if identicalSlots != availableA.Length()-1 {
				continue
			}
			for idxB, posB := range group.Positions {
				if idxA == idxB {
					continue
				}
				availableB := s.Availables[posB.Y][posB.X]
				if availableA.String() != availableB.String() {
					for _, number := range availableA.Availables() {
						if availableB.RemoveNumber(number) {
							changes++
						}
					}
				}
			}
		}
	}
	return changes
}

func (s *Sudoku) Resolve() error {
	changes := 0
	iteration := 0
	kind := "start"

start:
	if s.Debug {
		logrus.Infof("#######  iteration=%-3d changes=%-2d kind=%s\n%s\n%s", iteration, changes, kind, s.String(), s.AvailablesString())
		logrus.Infof(strings.Repeat("#", 42))
	}
	iteration++

	kind = "resolve-only-one"
	if changes = s.ResolveOnlyOne(); changes > 0 {
		goto start
	}

	kind = "resolve-numbers-that-can-only-be-in-few-positions"
	if changes = s.RemoveNumbersThatCanOnlyBeInFewPositions(); changes > 0 {
		goto start
	}

	return nil
}
<|endoftext|>