<commit_before>package dokugen

import (
	"log"
	"strings"
)

//TODO: Support non-squared DIMS (logic in Block() would need updating)
const BLOCK_DIM = 3
const DIM = BLOCK_DIM * BLOCK_DIM
const ROW_SEP = "\n"
const COL_SEP = "|"
const ALT_COL_SEP = "||"

type Grid struct {
	initalized bool
	cells      [DIM * DIM]Cell
	rows       [DIM][]*Cell
	cols       [DIM][]*Cell
	blocks     [DIM][]*Cell
	queue      *FiniteQueue
}

func NewGrid() *Grid {
	result := &Grid{}
	result.queue = NewFiniteQueue(1, DIM)
	i := 0
	for r := 0; r < DIM; r++ {
		for c := 0; c < DIM; c++ {
			result.cells[i] = NewCell(result, r, c)
			//The cell can't insert itself because it doesn't know where it will actually live in memory yet.
			result.queue.Insert(&result.cells[i])
			i++
		}
	}
	result.initalized = true
	return result
}

func LoadGrid(data string) *Grid {
	result := NewGrid()
	data = strings.Replace(data, ALT_COL_SEP, COL_SEP, -1)
	for r, row := range strings.Split(data, ROW_SEP) {
		for c, data := range strings.Split(row, COL_SEP) {
			cell := result.Cell(r, c)
			cell.Load(data)
		}
	}
	return result
}

func (self *Grid) Row(index int) []*Cell {
	if index < 0 || index >= DIM {
		log.Println("Invalid index passed to Row: ", index)
		return nil
	}
	if self.rows[index] == nil {
		self.rows[index] = self.cellList(index, 0, index, DIM-1)
	}
	return self.rows[index]
}

func (self *Grid) Col(index int) []*Cell {
	if index < 0 || index >= DIM {
		log.Println("Invalid index passed to Col: ", index)
		return nil
	}
	if self.cols[index] == nil {
		self.cols[index] = self.cellList(0, index, DIM-1, index)
	}
	return self.cols[index]
}

func (self *Grid) Block(index int) []*Cell {
	if index < 0 || index >= DIM {
		log.Println("Invalid index passed to Block: ", index)
		return nil
	}
	if self.blocks[index] == nil {
		//Conceptually, we'll pretend like the grid is made up of blocks that are arrayed with row/column
		//Once we find the block r/c, we'll multiply by the actual dim to get the upper left corner.

		blockCol := index % BLOCK_DIM
		blockRow := (index - blockCol) / BLOCK_DIM

		col := blockCol * BLOCK_DIM
		row := blockRow * BLOCK_DIM

		self.blocks[index] = self.cellList(row, col, row+BLOCK_DIM-1, col+BLOCK_DIM-1)
	}
	return self.blocks[index]
}

func (self *Grid) blockForCell(row int, col int) int {
	blockCol := col / BLOCK_DIM
	blockRow := row / BLOCK_DIM
	return blockRow*BLOCK_DIM + blockCol
}

func (self *Grid) Cell(row int, col int) *Cell {
	index := row*DIM + col
	if index >= DIM*DIM || index < 0 {
		log.Println("Invalid row/col index passed to Cell: ", row, ", ", col)
		return nil
	}
	return &self.cells[index]
}

func (self *Grid) cellList(rowOne int, colOne int, rowTwo int, colTwo int) []*Cell {
	length := (rowTwo - rowOne + 1) * (colTwo - colOne + 1)
	result := make([]*Cell, length)
	currentRow := rowOne
	currentCol := colOne
	for i := 0; i < length; i++ {
		result[i] = self.Cell(currentRow, currentCol)
		if colTwo > currentCol {
			currentCol++
		} else {
			if rowTwo > currentRow {
				currentRow++
				currentCol = colOne
			} else {
				//This should only happen the last time through the loop.
			}
		}
	}
	return result
}

//Fills in all of the cells it can without branching or doing any advanced
//techniques that require anything more than a single cell's possibles list.
func (self *Grid) fillSimpleCells() int {
	count := 0
	obj := self.queue.GetSmallerThan(2)
	for obj != nil {
		cell, ok := obj.(*Cell)
		if !ok {
			continue
		}
		cell.SetNumber(cell.implicitNumber())
		count++
		obj = self.queue.GetSmallerThan(2)
	}
	return count
}

func (self *Grid) DataString() string {
	var rows []string
	for r := 0; r < DIM; r++ {
		var row []string
		for c := 0; c < DIM; c++ {
			row = append(row, self.cells[r*DIM+c].DataString())
		}
		rows = append(rows, strings.Join(row, COL_SEP))
	}
	return strings.Join(rows, ROW_SEP)
}

func (self *Grid) String() string {
	var rows []string
	for r := 0; r < DIM; r++ {
		var row []string
		for c := 0; c < DIM; c++ {
			row = append(row, self.cells[r*DIM+c].String())
		}
		rows = append(rows, strings.Join(row, COL_SEP))
	}
	return strings.Join(rows, ROW_SEP)
}
<commit_msg>Factored out blockExtents so we can reuse the logic for diagram.<commit_after>package dokugen

import (
	"log"
	"strings"
)

//TODO: Support non-squared DIMS (logic in Block() would need updating)
const BLOCK_DIM = 3
const DIM = BLOCK_DIM * BLOCK_DIM
const ROW_SEP = "\n"
const COL_SEP = "|"
const ALT_COL_SEP = "||"

type Grid struct {
	initalized bool
	cells      [DIM * DIM]Cell
	rows       [DIM][]*Cell
	cols       [DIM][]*Cell
	blocks     [DIM][]*Cell
	queue      *FiniteQueue
}

func NewGrid() *Grid {
	result := &Grid{}
	result.queue = NewFiniteQueue(1, DIM)
	i := 0
	for r := 0; r < DIM; r++ {
		for c := 0; c < DIM; c++ {
			result.cells[i] = NewCell(result, r, c)
			//The cell can't insert itself because it doesn't know where it will actually live in memory yet.
			result.queue.Insert(&result.cells[i])
			i++
		}
	}
	result.initalized = true
	return result
}

func LoadGrid(data string) *Grid {
	result := NewGrid()
	data = strings.Replace(data, ALT_COL_SEP, COL_SEP, -1)
	for r, row := range strings.Split(data, ROW_SEP) {
		for c, data := range strings.Split(row, COL_SEP) {
			cell := result.Cell(r, c)
			cell.Load(data)
		}
	}
	return result
}

func (self *Grid) Row(index int) []*Cell {
	if index < 0 || index >= DIM {
		log.Println("Invalid index passed to Row: ", index)
		return nil
	}
	if self.rows[index] == nil {
		self.rows[index] = self.cellList(index, 0, index, DIM-1)
	}
	return self.rows[index]
}

func (self *Grid) Col(index int) []*Cell {
	if index < 0 || index >= DIM {
		log.Println("Invalid index passed to Col: ", index)
		return nil
	}
	if self.cols[index] == nil {
		self.cols[index] = self.cellList(0, index, DIM-1, index)
	}
	return self.cols[index]
}

func (self *Grid) Block(index int) []*Cell {
	if index < 0 || index >= DIM {
		log.Println("Invalid index passed to Block: ", index)
		return nil
	}
	if self.blocks[index] == nil {
		topRow, topCol, bottomRow, bottomCol := self.blockExtents(index)
		self.blocks[index] = self.cellList(topRow, topCol, bottomRow, bottomCol)
	}
	return self.blocks[index]
}

func (self *Grid) blockExtents(index int) (topRow int, topCol int, bottomRow int, bottomCol int) {
	//Conceptually, we'll pretend like the grid is made up of blocks that are arrayed with row/column
	//Once we find the block r/c, we'll multiply by the actual dim to get the upper left corner.

	blockCol := index % BLOCK_DIM
	blockRow := (index - blockCol) / BLOCK_DIM

	col := blockCol * BLOCK_DIM
	row := blockRow * BLOCK_DIM

	return row, col, row + BLOCK_DIM - 1, col + BLOCK_DIM - 1
}

func (self *Grid) blockForCell(row int, col int) int {
	blockCol := col / BLOCK_DIM
	blockRow := row / BLOCK_DIM
	return blockRow*BLOCK_DIM + blockCol
}

func (self *Grid) Cell(row int, col int) *Cell {
	index := row*DIM + col
	if index >= DIM*DIM || index < 0 {
		log.Println("Invalid row/col index passed to Cell: ", row, ", ", col)
		return nil
	}
	return &self.cells[index]
}

func (self *Grid) cellList(rowOne int, colOne int, rowTwo int, colTwo int) []*Cell {
	length := (rowTwo - rowOne + 1) * (colTwo - colOne + 1)
	result := make([]*Cell, length)
	currentRow := rowOne
	currentCol := colOne
	for i := 0; i < length; i++ {
		result[i] = self.Cell(currentRow, currentCol)
		if colTwo > currentCol {
			currentCol++
		} else {
			if rowTwo > currentRow {
				currentRow++
				currentCol = colOne
			} else {
				//This should only happen the last time through the loop.
			}
		}
	}
	return result
}

//Fills in all of the cells it can without branching or doing any advanced
//techniques that require anything more than a single cell's possibles list.
func (self *Grid) fillSimpleCells() int {
	count := 0
	obj := self.queue.GetSmallerThan(2)
	for obj != nil {
		cell, ok := obj.(*Cell)
		if !ok {
			continue
		}
		cell.SetNumber(cell.implicitNumber())
		count++
		obj = self.queue.GetSmallerThan(2)
	}
	return count
}

func (self *Grid) DataString() string {
	var rows []string
	for r := 0; r < DIM; r++ {
		var row []string
		for c := 0; c < DIM; c++ {
			row = append(row, self.cells[r*DIM+c].DataString())
		}
		rows = append(rows, strings.Join(row, COL_SEP))
	}
	return strings.Join(rows, ROW_SEP)
}

func (self *Grid) String() string {
	var rows []string
	for r := 0; r < DIM; r++ {
		var row []string
		for c := 0; c < DIM; c++ {
			row = append(row, self.cells[r*DIM+c].String())
		}
		rows = append(rows, strings.Join(row, COL_SEP))
	}
	return strings.Join(rows, ROW_SEP)
}
<|endoftext|>


<commit_before>package gsim

import (
	"math/big"
	"runtime"
	"sync"
)

// The OptionGenerator is responsible for generating the next
// available possible paths from each permutation prefix. Two
// implementations of OptionGenerator are provided: simplePermutation
// and graphPermutation. If neither are sufficient for your needs then
// you'll want to implement OptionGenerator yourself.
//
// If you do implement OptionGenerator yourself, you must ensure it is
// entirely deterministic. So do not rely on iteration order of maps
// and so forth.
type OptionGenerator interface {
	// Generate is provided with the previously-chosen option, and is
	// required to return the set of options now available as the next
	// element in the permutation. OptionGenerators are expected to be
	// stateful. Generate must return an empty list for permutation
	// generation to terminate.
	Generate(interface{}) []interface{}
	// Clone is used during permutation generation. If the
	// OptionGenerator is stateful then Clone must return a fresh
	// OptionGenerator which shares no mutable state with the receiver
	// of Clone.
	Clone() OptionGenerator
}

type node struct {
	n         *big.Int
	depth     int
	value     interface{}
	generator OptionGenerator
	cumuOpts  *big.Int
}

// Instances of PermutationConsumer may be supplied to the
// Permutations iteration functions: ForEach and ForEachPar.
type PermutationConsumer interface {
	// Clone is used only by Permutations.ForEachPar and is called
	// once for each go-routine which will be supplying permutations
	// to the PermutationConsumer. Through this, state can be
	// duplicated so that the consumer can be stateful and safe to
	// drive from multiple go-routines.
	Clone() PermutationConsumer
	// This function called once for each permutation generated.
	Consume(*big.Int, []interface{})
}

// Permutations allows you to interate through the available
// permutations, and extract specific permutations.
type Permutations node

var (
	bigIntZero = big.NewInt(0)
	bigIntOne  = big.NewInt(1)
)

// Construct a Permutations from an OptionGenerator.
func BuildPermutations(gen OptionGenerator) *Permutations {
	return (*Permutations)(&node{
		n:         bigIntZero,
		depth:     0,
		generator: gen,
		cumuOpts:  bigIntOne,
	})
}

type permN struct {
	perm []interface{}
	n    *big.Int
}

type parPermutationConsumer struct {
	ch        chan<- []*permN
	batch     []*permN
	batchIdx  int
	batchSize int
}

func (ppc *parPermutationConsumer) Clone() PermutationConsumer {
	return &parPermutationConsumer{
		ch:        ppc.ch,
		batch:     make([]*permN, ppc.batchSize),
		batchIdx:  0,
		batchSize: ppc.batchSize,
	}
}

func (ppc *parPermutationConsumer) Consume(n *big.Int, perm []interface{}) {
	permCopy := make([]interface{}, len(perm))
	copy(permCopy, perm)
	ppc.batch[ppc.batchIdx] = &permN{n: n, perm: permCopy}
	ppc.batchIdx += 1
	if ppc.batchIdx == ppc.batchSize {
		ppc.ch <- ppc.batch
		ppc.batch = make([]*permN, ppc.batchSize)
		ppc.batchIdx = 0
	}
}

func (ppc *parPermutationConsumer) flush() {
	if ppc.batchIdx > 0 {
		ppc.ch <- ppc.batch[:ppc.batchIdx]
		ppc.batch = make([]*permN, ppc.batchSize)
		ppc.batchIdx = 0
	}
}

// Iterate through every permutation and use concurrency. A number of
// go-routines will be spawned appropriate for the current value of
// GOMAXPROCS. These go-routines will be fed batches of permutations
// and then invoke f.Consume for each permutation. It's your
// responsibility to make sure f is safe to be run concurrently from
// multiple go-routines (see PermutationConsumer.Clone to see how
// stateful consumers can be built).
//
// If the batchsize is very low, then the generation of permutations
// will thrash CPU due to contention for locks on channels. If your
// processing of each permutation is very quick, then high numbers
// (e.g. 8192) can help to keep your CPU busy. If your processing of
// each permutation is less quick then lower numbers will avoid memory
// ballooning. Some trial and error may be worthwhile to find a good
// number for your computer, but 2048 is a sensible place to start.
func (p *Permutations) ForEachPar(batchSize int, f PermutationConsumer) {
	par := runtime.GOMAXPROCS(0) // 0 gets the current count
	var wg sync.WaitGroup
	wg.Add(par)
	ch := make(chan []*permN, par*par)

	for idx := 0; idx < par; idx += 1 {
		go func() {
			defer wg.Done()
			g := f.Clone()
			for {
				perms, ok := <-ch
				if !ok {
					return
				}
				for _, perm := range perms {
					g.Consume(perm.n, perm.perm)
				}
			}
		}()
	}

	ppc := &parPermutationConsumer{
		ch:        ch,
		batch:     make([]*permN, batchSize),
		batchIdx:  0,
		batchSize: batchSize,
	}
	p.ForEach(ppc)
	ppc.flush()
	close(ch)
	wg.Wait()
}

// Iterate through every permutation in the current go-routine. No
// parallelism occurs. The function f.Consume is invoked once for
// every permutation. It is supplied with the permutation number, and
// the permutation itself. These arguments should be considered
// read-only. If you mutate the permutation number or permutation then
// behaviour is undefined.
func (p *Permutations) ForEach(f PermutationConsumer) {
	perm := []interface{}{}

	worklist := []*node{&node{
		n:         p.n,
		depth:     p.depth,
		generator: p.generator.Clone(),
		cumuOpts:  p.cumuOpts,
	}}
	l := len(worklist)

	for l != 0 {
		l -= 1
		cur := worklist[l]
		worklist = worklist[:l]

		perm = append(perm[:cur.depth], cur.value)

		options := cur.generator.Generate(cur.value)
		optionCount := len(options)

		if optionCount == 0 {
			f.Consume(cur.n, perm[1:])

		} else {
			cumuOpts := big.NewInt(int64(optionCount))
			cumuOpts.Mul(cur.cumuOpts, cumuOpts)
			for idx, option := range options {
				var childN *big.Int
				if optionCount == 1 {
					childN = cur.n
				} else {
					childN = big.NewInt(int64(idx))
					childN.Mul(childN, cur.cumuOpts)
					childN.Add(childN, cur.n)
				}
				var gen OptionGenerator
				if idx == 0 {
					gen = cur.generator
				} else {
					gen = cur.generator.Clone()
				}
				child := &node{
					n:         childN,
					depth:     cur.depth + 1,
					value:     option,
					generator: gen,
					cumuOpts:  cumuOpts,
				}
				worklist = append(worklist, child)
			}
			l += optionCount
		}
	}
}

// Every permutation has a unique number, which is supplied to the
// function passed to both of the iteration functions in
// Permutations. If you need to generate specific permutations, those
// numbers can be provided to Permutation, which will generate the
// exact same permutation. Note that iterating through a range of
// permutation numbers and repeatedly calling Permutation is slower
// than using either of the iterator functions.
func (p *Permutations) Permutation(permNum *big.Int) []interface{} {
	n := big.NewInt(0).Set(permNum)
	perm := []interface{}{}
	choiceBig := big.NewInt(0)

	gen := p.generator.Clone()
	val := p.value
	for {
		options := gen.Generate(val)
		optionCount := len(options)
		if optionCount == 0 {
			return perm
		}
		choiceBig.SetInt64(int64(optionCount))
		n.QuoRem(n, choiceBig, choiceBig)
		val = options[int(choiceBig.Int64())]
		perm = append(perm, val)
		gen = gen.Clone()
	}
	return perm
}
<commit_msg>Avoid unnecessary double dereferencing, and keep life simpler for GC.<commit_after>package gsim

import (
	"math/big"
	"runtime"
	"sync"
)

// The OptionGenerator is responsible for generating the next
// available possible paths from each permutation prefix. Two
// implementations of OptionGenerator are provided: simplePermutation
// and graphPermutation. If neither are sufficient for your needs then
// you'll want to implement OptionGenerator yourself.
//
// If you do implement OptionGenerator yourself, you must ensure it is
// entirely deterministic. So do not rely on iteration order of maps
// and so forth.
type OptionGenerator interface {
	// Generate is provided with the previously-chosen option, and is
	// required to return the set of options now available as the next
	// element in the permutation. OptionGenerators are expected to be
	// stateful. Generate must return an empty list for permutation
	// generation to terminate.
	Generate(interface{}) []interface{}
	// Clone is used during permutation generation. If the
	// OptionGenerator is stateful then Clone must return a fresh
	// OptionGenerator which shares no mutable state with the receiver
	// of Clone.
	Clone() OptionGenerator
}

type node struct {
	n         *big.Int
	depth     int
	value     interface{}
	generator OptionGenerator
	cumuOpts  *big.Int
}

// Instances of PermutationConsumer may be supplied to the
// Permutations iteration functions: ForEach and ForEachPar.
type PermutationConsumer interface {
	// Clone is used only by Permutations.ForEachPar and is called
	// once for each go-routine which will be supplying permutations
	// to the PermutationConsumer. Through this, state can be
	// duplicated so that the consumer can be stateful and safe to
	// drive from multiple go-routines.
	Clone() PermutationConsumer
	// This function called once for each permutation generated.
	Consume(*big.Int, []interface{})
}

// Permutations allows you to interate through the available
// permutations, and extract specific permutations.
type Permutations node

var (
	bigIntZero = big.NewInt(0)
	bigIntOne  = big.NewInt(1)
)

// Construct a Permutations from an OptionGenerator.
func BuildPermutations(gen OptionGenerator) *Permutations {
	return (*Permutations)(&node{
		n:         bigIntZero,
		depth:     0,
		generator: gen,
		cumuOpts:  bigIntOne,
	})
}

type permN struct {
	perm []interface{}
	n    *big.Int
}

type parPermutationConsumer struct {
	ch        chan<- []permN
	batch     []permN
	batchIdx  int
	batchSize int
}

func (ppc *parPermutationConsumer) Clone() PermutationConsumer {
	return &parPermutationConsumer{
		ch:        ppc.ch,
		batch:     make([]permN, ppc.batchSize),
		batchIdx:  0,
		batchSize: ppc.batchSize,
	}
}

func (ppc *parPermutationConsumer) Consume(n *big.Int, perm []interface{}) {
	permCopy := make([]interface{}, len(perm))
	copy(permCopy, perm)
	ppc.batch[ppc.batchIdx].n = n
	ppc.batch[ppc.batchIdx].perm = permCopy
	ppc.batchIdx += 1
	if ppc.batchIdx == ppc.batchSize {
		ppc.ch <- ppc.batch
		ppc.batch = make([]permN, ppc.batchSize)
		ppc.batchIdx = 0
	}
}

func (ppc *parPermutationConsumer) flush() {
	if ppc.batchIdx > 0 {
		ppc.ch <- ppc.batch[:ppc.batchIdx]
		ppc.batch = make([]permN, ppc.batchSize)
		ppc.batchIdx = 0
	}
}

// Iterate through every permutation and use concurrency. A number of
// go-routines will be spawned appropriate for the current value of
// GOMAXPROCS. These go-routines will be fed batches of permutations
// and then invoke f.Consume for each permutation. It's your
// responsibility to make sure f is safe to be run concurrently from
// multiple go-routines (see PermutationConsumer.Clone to see how
// stateful consumers can be built).
//
// If the batchsize is very low, then the generation of permutations
// will thrash CPU due to contention for locks on channels. If your
// processing of each permutation is very quick, then high numbers
// (e.g. 8192) can help to keep your CPU busy. If your processing of
// each permutation is less quick then lower numbers will avoid memory
// ballooning. Some trial and error may be worthwhile to find a good
// number for your computer, but 2048 is a sensible place to start.
func (p *Permutations) ForEachPar(batchSize int, f PermutationConsumer) {
	par := runtime.GOMAXPROCS(0) // 0 gets the current count
	var wg sync.WaitGroup
	wg.Add(par)
	ch := make(chan []permN, par*par)

	for idx := 0; idx < par; idx += 1 {
		go func() {
			defer wg.Done()
			g := f.Clone()
			for {
				perms, ok := <-ch
				if !ok {
					return
				}
				for _, perm := range perms {
					g.Consume(perm.n, perm.perm)
				}
			}
		}()
	}

	ppc := &parPermutationConsumer{
		ch:        ch,
		batch:     make([]permN, batchSize),
		batchIdx:  0,
		batchSize: batchSize,
	}
	p.ForEach(ppc)
	ppc.flush()
	close(ch)
	wg.Wait()
}

// Iterate through every permutation in the current go-routine. No
// parallelism occurs. The function f.Consume is invoked once for
// every permutation. It is supplied with the permutation number, and
// the permutation itself. These arguments should be considered
// read-only. If you mutate the permutation number or permutation then
// behaviour is undefined.
func (p *Permutations) ForEach(f PermutationConsumer) {
	perm := []interface{}{}

	worklist := []*node{&node{
		n:         p.n,
		depth:     p.depth,
		generator: p.generator.Clone(),
		cumuOpts:  p.cumuOpts,
	}}
	l := len(worklist)

	for l != 0 {
		l -= 1
		cur := worklist[l]
		worklist = worklist[:l]

		perm = append(perm[:cur.depth], cur.value)

		options := cur.generator.Generate(cur.value)
		optionCount := len(options)

		if optionCount == 0 {
			f.Consume(cur.n, perm[1:])

		} else {
			cumuOpts := big.NewInt(int64(optionCount))
			cumuOpts.Mul(cur.cumuOpts, cumuOpts)
			for idx, option := range options {
				var childN *big.Int
				if optionCount == 1 {
					childN = cur.n
				} else {
					childN = big.NewInt(int64(idx))
					childN.Mul(childN, cur.cumuOpts)
					childN.Add(childN, cur.n)
				}
				var gen OptionGenerator
				if idx == 0 {
					gen = cur.generator
				} else {
					gen = cur.generator.Clone()
				}
				child := &node{
					n:         childN,
					depth:     cur.depth + 1,
					value:     option,
					generator: gen,
					cumuOpts:  cumuOpts,
				}
				worklist = append(worklist, child)
			}
			l += optionCount
		}
	}
}

// Every permutation has a unique number, which is supplied to the
// function passed to both of the iteration functions in
// Permutations. If you need to generate specific permutations, those
// numbers can be provided to Permutation, which will generate the
// exact same permutation. Note that iterating through a range of
// permutation numbers and repeatedly calling Permutation is slower
// than using either of the iterator functions.
func (p *Permutations) Permutation(permNum *big.Int) []interface{} {
	n := big.NewInt(0).Set(permNum)
	perm := []interface{}{}
	choiceBig := big.NewInt(0)

	gen := p.generator.Clone()
	val := p.value
	for {
		options := gen.Generate(val)
		optionCount := len(options)
		if optionCount == 0 {
			return perm
		}
		choiceBig.SetInt64(int64(optionCount))
		n.QuoRem(n, choiceBig, choiceBig)
		val = options[int(choiceBig.Int64())]
		perm = append(perm, val)
		gen = gen.Clone()
	}
	return perm
}
<|endoftext|>


<commit_before>// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

// go test *.go -bench=".*"

package gaes_test

import (
	"testing"

	"github.com/gogf/gf/g/crypto/gaes"
	"github.com/gogf/gf/g/test/gtest"
)

var (
	content = []byte("pibigstar")
	// iv 长度必须等于blockSize，只能为16
	iv     = []byte("Hello My GoFrame")
	key_16 = []byte("1234567891234567")
	key_24 = []byte("123456789123456789123456")
	key_32 = []byte("12345678912345678912345678912345")
	keys   = []byte("12345678912345678912345678912346")
)

func TestEncrypt(t *testing.T) {
	gtest.Case(t, func() {
		_, err := gaes.Encrypt(content, key_16)
		gtest.Assert(err, nil)
		_, err = gaes.Encrypt(content, key_24)
		gtest.Assert(err, nil)
		_, err = gaes.Encrypt(content, key_32)
		gtest.Assert(err, nil)
		_, err = gaes.Encrypt(content, key_16, iv)
		gtest.Assert(err, nil)
	})
}

func TestDecrypt(t *testing.T) {
	gtest.Case(t, func() {
		encrypt, err := gaes.Encrypt(content, key_16)
		decrypt, err := gaes.Decrypt(encrypt, key_16)
		gtest.Assert(err, nil)
		gtest.Assert(string(decrypt), string(content))

		encrypt, err = gaes.Encrypt(content, key_24)
		decrypt, err = gaes.Decrypt(encrypt, key_24)
		gtest.Assert(err, nil)
		gtest.Assert(string(decrypt), string(content))

		encrypt, err = gaes.Encrypt(content, key_32)
		decrypt, err = gaes.Decrypt(encrypt, key_32)
		gtest.Assert(err, nil)
		gtest.Assert(string(decrypt), string(content))

		encrypt, err = gaes.Encrypt(content, key_32, iv)
		decrypt, err = gaes.Decrypt(encrypt, key_32, iv)
		gtest.Assert(err, nil)
		gtest.Assert(string(decrypt), string(content))

		encrypt, err = gaes.Encrypt(content, key_32, iv)
		decrypt, err = gaes.Decrypt(encrypt, keys, iv)
		gtest.Assert(err, "invalid padding")
	})
}
<commit_msg>add gaes test<commit_after>// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

// go test *.go -bench=".*"

package gaes_test

import (
	"testing"

	"github.com/gogf/gf/g/crypto/gaes"
	"github.com/gogf/gf/g/test/gtest"
)

var (
	content = []byte("pibigstar")
	// iv 长度必须等于blockSize，只能为16
	iv         = []byte("Hello My GoFrame")
	key_16     = []byte("1234567891234567")
	key_17     = []byte("12345678912345670")
	key_24     = []byte("123456789123456789123456")
	key_32     = []byte("12345678912345678912345678912345")
	keys       = []byte("12345678912345678912345678912346")
	key_err    = []byte("1234")
	key_32_err = []byte("1234567891234567891234567891234 ")
)

func TestEncrypt(t *testing.T) {
	gtest.Case(t, func() {
		_, err := gaes.Encrypt(content, key_16)
		gtest.Assert(err, nil)
		_, err = gaes.Encrypt(content, key_24)
		gtest.Assert(err, nil)
		_, err = gaes.Encrypt(content, key_32)
		gtest.Assert(err, nil)
		_, err = gaes.Encrypt(content, key_16, iv)
		gtest.Assert(err, nil)
	})
}

func TestEncryptErr(t *testing.T) {
	gtest.Case(t, func() {
		// encrypt key error
		_, err := gaes.Encrypt(content, key_err)
		gtest.AssertNE(err, nil)
	})
}

func TestDecryptErr(t *testing.T) {
	gtest.Case(t, func() {
		// decrypt key error
		encrypt, err := gaes.Encrypt(content, key_16)
		_, err = gaes.Decrypt(encrypt, key_err)
		gtest.AssertNE(err, nil)

		// decrypt content too short error
		_, err = gaes.Decrypt([]byte("test"), key_16)
		gtest.AssertNE(err, nil)

		// decrypt content size error
		_, err = gaes.Decrypt(key_17, key_16)
		gtest.AssertNE(err, nil)
	})
}

func TestPKCS5UnPaddingErr(t *testing.T) {
	gtest.Case(t, func() {
		// PKCS5UnPadding blockSize zero
		_, err := gaes.PKCS5UnPadding(content, 0)
		gtest.AssertNE(err, nil)

		// PKCS5UnPadding src len zero
		_, err = gaes.PKCS5UnPadding([]byte(""), 16)
		gtest.AssertNE(err, nil)

		// PKCS5UnPadding src len > blockSize
		_, err = gaes.PKCS5UnPadding(key_17, 16)
		gtest.AssertNE(err, nil)

		// PKCS5UnPadding src len > blockSize
		_, err = gaes.PKCS5UnPadding(key_32_err, 32)
		gtest.AssertNE(err, nil)
	})
}

func TestDecrypt(t *testing.T) {
	gtest.Case(t, func() {
		encrypt, err := gaes.Encrypt(content, key_16)
		decrypt, err := gaes.Decrypt(encrypt, key_16)
		gtest.Assert(err, nil)
		gtest.Assert(string(decrypt), string(content))

		encrypt, err = gaes.Encrypt(content, key_24)
		decrypt, err = gaes.Decrypt(encrypt, key_24)
		gtest.Assert(err, nil)
		gtest.Assert(string(decrypt), string(content))

		encrypt, err = gaes.Encrypt(content, key_32)
		decrypt, err = gaes.Decrypt(encrypt, key_32)
		gtest.Assert(err, nil)
		gtest.Assert(string(decrypt), string(content))

		encrypt, err = gaes.Encrypt(content, key_32, iv)
		decrypt, err = gaes.Decrypt(encrypt, key_32, iv)
		gtest.Assert(err, nil)
		gtest.Assert(string(decrypt), string(content))

		encrypt, err = gaes.Encrypt(content, key_32, iv)
		decrypt, err = gaes.Decrypt(encrypt, keys, iv)
		gtest.Assert(err, "invalid padding")
	})
}
<|endoftext|>


<commit_before>package hdf5

// #include "hdf5.h"
// #include "hdf5_hl.h"
// #include <stdlib.h>
// #include <string.h>
import "C"

import (
	"fmt"

	"reflect"
	"runtime"
	"unsafe"
)

type Table struct {
	Location
}

func newPacketTable(id C.hid_t) *Table {
	t := &Table{Location{id}}
	runtime.SetFinalizer(t, (*Table).finalizer)
	return t
}

func (t *Table) finalizer() {
	err := t.Close()
	if err != nil {
		panic(fmt.Sprintf("error closing packet table: %s", err))
	}
}

// Close closes an open packet table.
func (t *Table) Close() error {
	if t.id > 0 {
		err := h5err(C.H5PTclose(t.id))
		if err != nil {
			t.id = 0
		}
		return err
	}
	return nil
}

// IsValid returns whether or not an indentifier points to a packet table.
func (t *Table) IsValid() bool {
	o := int(C.H5PTis_valid(t.id))
	if o < 0 {
		return false
	}
	return true
}

func (t *Table) Id() int {
	return int(t.id)
}

// ReadPackets reads a number of packets from a packet table.
func (t *Table) ReadPackets(start, nrecords int, data interface{}) error {
	c_start := C.hsize_t(start)
	c_nrecords := C.size_t(nrecords)
	rt := reflect.TypeOf(data)
	rv := reflect.ValueOf(data)
	c_data := unsafe.Pointer(nil)
	switch rt.Kind() {
	case reflect.Array:
		if rv.Cap() < nrecords {
			panic(fmt.Sprintf("not enough capacity in array (cap=%d)", rv.Cap()))
		}
		c_data = unsafe.Pointer(rv.Index(0).UnsafeAddr())

	default:
		panic(fmt.Sprintf("unhandled kind (%s), need array", rt.Kind()))
	}
	err := C.H5PTread_packets(t.id, c_start, c_nrecords, c_data)
	return h5err(err)
}

// Append appends packets to the end of a packet table.
func (t *Table) Append(data interface{}) error {
	rt := reflect.TypeOf(data)
	v := reflect.ValueOf(data)
	c_nrecords := C.size_t(0)
	c_data := unsafe.Pointer(nil)

	switch rt.Kind() {

	case reflect.Array:
		c_nrecords = C.size_t(v.Len())
		c_data = unsafe.Pointer(v.UnsafeAddr())

	case reflect.String:
		c_nrecords = C.size_t(v.Len())
		str := (*reflect.StringHeader)(unsafe.Pointer(v.UnsafeAddr()))
		c_data = unsafe.Pointer(str.Data)

	case reflect.Ptr:
		c_nrecords = C.size_t(1)
		c_data = unsafe.Pointer(v.Elem().UnsafeAddr())

	default:
		c_nrecords = C.size_t(1)
		c_data = unsafe.Pointer(v.UnsafeAddr())
	}

	err := C.H5PTappend(t.id, c_nrecords, c_data)
	return h5err(err)
}

// Next reads packets from a packet table starting at the current index.
func (t *Table) Next(data interface{}) error {
	rt := reflect.TypeOf(data)
	rv := reflect.ValueOf(data)
	n := C.size_t(0)
	cdata := unsafe.Pointer(nil)
	switch rt.Kind() {
	case reflect.Array:
		if rv.Cap() <= 0 {
			panic(fmt.Sprintf("not enough capacity in array (cap=%d)", rv.Cap()))
		}
		cdata = unsafe.Pointer(rv.Index(0).UnsafeAddr())
		n = C.size_t(rv.Cap())
	default:
		panic(fmt.Sprintf("unsupported kind (%s), need slice or array", rt.Kind()))
	}
	err := C.H5PTget_next(t.id, n, cdata)
	return h5err(err)
}

// NumPackets returns the number of packets in a packet table.
func (t *Table) NumPackets() (int, error) {
	c_nrecords := C.hsize_t(0)
	err := C.H5PTget_num_packets(t.id, &c_nrecords)
	return int(c_nrecords), h5err(err)
}

// CreateIndex resets a packet table's index to the first packet.
func (t *Table) CreateIndex() error {
	err := C.H5PTcreate_index(t.id)
	return h5err(err)
}

// SetIndex sets a packet table's index.
func (t *Table) SetIndex(index int) error {
	c_idx := C.hsize_t(index)
	err := C.H5PTset_index(t.id, c_idx)
	return h5err(err)
}

// Type returns an identifier for a copy of the datatype for a dataset.
func (t *Table) Type() (*Datatype, error) {
	hid := C.H5Dget_type(t.id)
	err := h5err(C.herr_t(int(hid)))
	if err != nil {
		return nil, err
	}
	dt := NewDatatype(hid, nil)
	return dt, err
}

func createTable(id C.hid_t, name string, dtype *Datatype, chunkSize, compression int) (*Table, error) {
	c_name := C.CString(name)
	defer C.free(unsafe.Pointer(c_name))

	chunk := C.hsize_t(chunkSize)
	compr := C.int(compression)
	hid := C.H5PTcreate_fl(id, c_name, dtype.id, chunk, compr)
	err := h5err(C.herr_t(int(hid)))
	if err != nil {
		return nil, err
	}
	table := newPacketTable(hid)
	return table, err
}

func createTableFrom(id C.hid_t, name string, dtype interface{}, chunkSize, compression int) (*Table, error) {
	switch dt := dtype.(type) {
	case reflect.Type:
		hdfDtype := newDataTypeFromType(dt)
		return createTable(id, name, hdfDtype, chunkSize, compression)
	case *Datatype:
		return createTable(id, name, dt, chunkSize, compression)
	default:
		hdfDtype := newDataTypeFromType(reflect.TypeOf(dtype))
		return createTable(id, name, hdfDtype, chunkSize, compression)
	}
}

func openTable(id C.hid_t, name string) (*Table, error) {
	c_name := C.CString(name)
	defer C.free(unsafe.Pointer(c_name))

	hid := C.H5PTopen(id, c_name)
	err := h5err(C.herr_t(int(hid)))
	if err != nil {
		return nil, err
	}
	table := newPacketTable(hid)
	return table, err
}
<commit_msg>h5pt: proper handling of slices<commit_after>package hdf5

// #include "hdf5.h"
// #include "hdf5_hl.h"
// #include <stdlib.h>
// #include <string.h>
import "C"

import (
	"fmt"

	"reflect"
	"runtime"
	"unsafe"
)

type Table struct {
	Location
}

func newPacketTable(id C.hid_t) *Table {
	t := &Table{Location{id}}
	runtime.SetFinalizer(t, (*Table).finalizer)
	return t
}

func (t *Table) finalizer() {
	err := t.Close()
	if err != nil {
		panic(fmt.Sprintf("error closing packet table: %s", err))
	}
}

// Close closes an open packet table.
func (t *Table) Close() error {
	if t.id > 0 {
		err := h5err(C.H5PTclose(t.id))
		if err != nil {
			t.id = 0
		}
		return err
	}
	return nil
}

// IsValid returns whether or not an indentifier points to a packet table.
func (t *Table) IsValid() bool {
	o := int(C.H5PTis_valid(t.id))
	if o < 0 {
		return false
	}
	return true
}

func (t *Table) Id() int {
	return int(t.id)
}

// ReadPackets reads a number of packets from a packet table.
func (t *Table) ReadPackets(start, nrecords int, data interface{}) error {
	c_start := C.hsize_t(start)
	c_nrecords := C.size_t(nrecords)
	rv := reflect.Indirect(reflect.ValueOf(data))
	rt := rv.Type()
	c_data := unsafe.Pointer(nil)
	switch rt.Kind() {
	case reflect.Array:
		if rv.Len() < nrecords {
			panic(fmt.Sprintf("not enough capacity in array (cap=%d)", rv.Len()))
		}
		c_data = unsafe.Pointer(rv.Index(0).UnsafeAddr())

	case reflect.Slice:
		if rv.Len() < nrecords {
			panic(fmt.Sprintf("not enough capacity in slice (cap=%d)", rv.Len()))
		}
		slice := (*reflect.SliceHeader)(unsafe.Pointer(rv.UnsafeAddr()))
		c_data = unsafe.Pointer(slice.Data)

	default:
		panic(fmt.Sprintf("unhandled kind (%s), need slice or array", rt.Kind()))
	}
	err := C.H5PTread_packets(t.id, c_start, c_nrecords, c_data)
	return h5err(err)
}

// Append appends packets to the end of a packet table.
func (t *Table) Append(data interface{}) error {
	rv := reflect.Indirect(reflect.ValueOf(data))
	rt := rv.Type()
	c_nrecords := C.size_t(0)
	c_data := unsafe.Pointer(nil)

	switch rt.Kind() {

	case reflect.Array:
		c_nrecords = C.size_t(rv.Len())
		c_data = unsafe.Pointer(rv.UnsafeAddr())

	case reflect.Slice:
		c_nrecords = C.size_t(rv.Len())
		slice := (*reflect.SliceHeader)(unsafe.Pointer(rv.UnsafeAddr()))
		c_data = unsafe.Pointer(slice.Data)

	case reflect.String:
		c_nrecords = C.size_t(rv.Len())
		str := (*reflect.StringHeader)(unsafe.Pointer(rv.UnsafeAddr()))
		c_data = unsafe.Pointer(str.Data)

	case reflect.Ptr:
		c_nrecords = C.size_t(1)
		c_data = unsafe.Pointer(rv.Elem().UnsafeAddr())

	default:
		c_nrecords = C.size_t(1)
		c_data = unsafe.Pointer(rv.UnsafeAddr())
	}

	err := C.H5PTappend(t.id, c_nrecords, c_data)
	return h5err(err)
}

// Next reads packets from a packet table starting at the current index.
func (t *Table) Next(data interface{}) error {
	rt := reflect.TypeOf(data)
	rv := reflect.ValueOf(data)
	n := C.size_t(0)
	cdata := unsafe.Pointer(nil)
	switch rt.Kind() {
	case reflect.Array:
		if rv.Cap() <= 0 {
			panic(fmt.Sprintf("not enough capacity in array (cap=%d)", rv.Cap()))
		}
		cdata = unsafe.Pointer(rv.Index(0).UnsafeAddr())
		n = C.size_t(rv.Cap())
	case reflect.Slice:
		if rv.Cap() <= 0 {
			panic(fmt.Sprintf("not enough capacity in slice (cap=%d)", rv.Cap()))
		}
		cdata = unsafe.Pointer(rv.Index(0).UnsafeAddr())
		n = C.size_t(rv.Cap())
	default:
		panic(fmt.Sprintf("unsupported kind (%s), need slice or array", rt.Kind()))
	}
	err := C.H5PTget_next(t.id, n, cdata)
	return h5err(err)
}

// NumPackets returns the number of packets in a packet table.
func (t *Table) NumPackets() (int, error) {
	c_nrecords := C.hsize_t(0)
	err := C.H5PTget_num_packets(t.id, &c_nrecords)
	return int(c_nrecords), h5err(err)
}

// CreateIndex resets a packet table's index to the first packet.
func (t *Table) CreateIndex() error {
	err := C.H5PTcreate_index(t.id)
	return h5err(err)
}

// SetIndex sets a packet table's index.
func (t *Table) SetIndex(index int) error {
	c_idx := C.hsize_t(index)
	err := C.H5PTset_index(t.id, c_idx)
	return h5err(err)
}

// Type returns an identifier for a copy of the datatype for a dataset.
func (t *Table) Type() (*Datatype, error) {
	hid := C.H5Dget_type(t.id)
	err := h5err(C.herr_t(int(hid)))
	if err != nil {
		return nil, err
	}
	dt := NewDatatype(hid, nil)
	return dt, err
}

func createTable(id C.hid_t, name string, dtype *Datatype, chunkSize, compression int) (*Table, error) {
	c_name := C.CString(name)
	defer C.free(unsafe.Pointer(c_name))

	chunk := C.hsize_t(chunkSize)
	compr := C.int(compression)
	hid := C.H5PTcreate_fl(id, c_name, dtype.id, chunk, compr)
	err := h5err(C.herr_t(int(hid)))
	if err != nil {
		return nil, err
	}
	table := newPacketTable(hid)
	return table, err
}

func createTableFrom(id C.hid_t, name string, dtype interface{}, chunkSize, compression int) (*Table, error) {
	switch dt := dtype.(type) {
	case reflect.Type:
		hdfDtype := newDataTypeFromType(dt)
		return createTable(id, name, hdfDtype, chunkSize, compression)
	case *Datatype:
		return createTable(id, name, dt, chunkSize, compression)
	default:
		hdfDtype := newDataTypeFromType(reflect.TypeOf(dtype))
		return createTable(id, name, hdfDtype, chunkSize, compression)
	}
}

func openTable(id C.hid_t, name string) (*Table, error) {
	c_name := C.CString(name)
	defer C.free(unsafe.Pointer(c_name))

	hid := C.H5PTopen(id, c_name)
	err := h5err(C.herr_t(int(hid)))
	if err != nil {
		return nil, err
	}
	table := newPacketTable(hid)
	return table, err
}
<|endoftext|>


<commit_before>package validity

import (
	"errors"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// SpecialGuard is a validator for string types
type SpecialGuard struct {
	Value string
	Rules []string
}

// Check ensures that the value is ok
func (guard SpecialGuard) Check() Result {
	result := Result{
		Errors:  []string{},
		IsValid: true,
	}
	for _, rule := range guard.Rules {
		isValid, err := guard.checkRule(rule)
		if err != nil {
			panic(err)
		}
		if !isValid {
			result.Errors = append(result.Errors, "SPECIAL#"+rule)
			result.IsValid = false
		}
	}
	return result
}

func (guard SpecialGuard) checkRule(rule string) (bool, error) {
	switch rule {
	case SpecialIBAN:
		return guard.validateIBAN(), nil
	case SpecialCIF:
		return guard.validateCIF(), nil
	case SpecialCnp:
		return guard.validateCNP(), nil
	case SpecialShortDate:
		return guard.validateShortDate(), nil
	case SpecialLongDate:
		return guard.validateLongDate(), nil
	case SpecialEmail:
		return guard.validateEmail(), nil
	}
	return false, errors.New("The guardian SPECIAL does not have the rule [" + rule + "]")
}

func (guard SpecialGuard) toInt(s string) int {
	out, _ := strconv.ParseInt(s, 10, 64)

	return int(out)
}

func (guard SpecialGuard) checkRegexp(r string) bool {
	expression, _ := regexp.Compile(r)

	return expression.MatchString(guard.Value)
}

// validateIBAN validates a bank account
// It must NOT have whitespaces
func (guard SpecialGuard) validateIBAN() bool {

	iban := strings.ToUpper(guard.Value)

	if len(iban) < 10 {
		// log.Println("The IBAN must have at least 10 characters")
		return false
	}

	countrycode := iban[0:2]

	// Check the country code and find the country specific format
	bbancountrypatterns := map[string]string{
		// "AL": "\\d{8}[\\dA-Z]{16}",
		// "AD": "\\d{8}[\\dA-Z]{12}",
		// "AT": "\\d{16}",
		// "AZ": "[\\dA-Z]{4}\\d{20}",
		// "BE": "\\d{12}",
		// "BH": "[A-Z]{4}[\\dA-Z]{14}",
		// "BA": "\\d{16}",
		// "BR": "\\d{23}[A-Z][\\dA-Z]",
		// "BG": "[A-Z]{4}\\d{6}[\\dA-Z]{8}",
		// "CR": "\\d{17}",
		// "HR": "\\d{17}",
		// "CY": "\\d{8}[\\dA-Z]{16}",
		// "CZ": "\\d{20}",
		// "DK": "\\d{14}",
		// "DO": "[A-Z]{4}\\d{20}",
		// "EE": "\\d{16}",
		// "FO": "\\d{14}",
		// "FI": "\\d{14}",
		// "FR": "\\d{10}[\\dA-Z]{11}\\d{2}",
		// "GE": "[\\dA-Z]{2}\\d{16}",
		// "DE": "\\d{18}",
		// "GI": "[A-Z]{4}[\\dA-Z]{15}",
		// "GR": "\\d{7}[\\dA-Z]{16}",
		// "GL": "\\d{14}",
		// "GT": "[\\dA-Z]{4}[\\dA-Z]{20}",
		// "HU": "\\d{24}",
		// "IS": "\\d{22}",
		// "IE": "[\\dA-Z]{4}\\d{14}",
		// "IL": "\\d{19}",
		// "IT": "[A-Z]\\d{10}[\\dA-Z]{12}",
		// "KZ": "\\d{3}[\\dA-Z]{13}",
		// "KW": "[A-Z]{4}[\\dA-Z]{22}",
		// "LV": "[A-Z]{4}[\\dA-Z]{13}",
		// "LB": "\\d{4}[\\dA-Z]{20}",
		// "LI": "\\d{5}[\\dA-Z]{12}",
		// "LT": "\\d{16}",
		// "LU": "\\d{3}[\\dA-Z]{13}",
		// "MK": "\\d{3}[\\dA-Z]{10}\\d{2}",
		// "MT": "[A-Z]{4}\\d{5}[\\dA-Z]{18}",
		// "MR": "\\d{23}",
		// "MU": "[A-Z]{4}\\d{19}[A-Z]{3}",
		// "MC": "\\d{10}[\\dA-Z]{11}\\d{2}",
		// "MD": "[\\dA-Z]{2}\\d{18}",
		// "ME": "\\d{18}",
		// "NL": "[A-Z]{4}\\d{10}",
		// "NO": "\\d{11}",
		// "PK": "[\\dA-Z]{4}\\d{16}",
		// "PS": "[\\dA-Z]{4}\\d{21}",
		// "PL": "\\d{24}",
		// "PT": "\\d{21}",
		// "SM": "[A-Z]\\d{10}[\\dA-Z]{12}",
		// "SA": "\\d{2}[\\dA-Z]{18}",
		// "RS": "\\d{18}",
		// "SK": "\\d{20}",
		// "SI": "\\d{15}",
		// "ES": "\\d{20}",
		// "SE": "\\d{20}",
		// "CH": "\\d{5}[\\dA-Z]{12}",
		// "TN": "\\d{20}",
		// "TR": "\\d{5}[\\dA-Z]{17}",
		// "AE": "\\d{3}\\d{16}",
		// "GB": "[A-Z]{4}\\d{14}",
		// "VG": "[\\dA-Z]{4}\\d{16}",
		"RO": "[A-Z]{4}[\\dA-Z]{16}",
	}

	_, isInTheList := bbancountrypatterns[countrycode]

	if !isInTheList {
		// log.Println("The country code is not in the list")
		return false
	}

	// // As new countries will start using IBAN in the
	// // future, we only check if the countrycode is known.
	// // This prevents false negatives, while almost all
	// // false positives introduced by this, will be caught
	// // by the checksum validation below anyway.
	// // Strict checking should return FALSE for unknown
	// // countries.
	// if (typeof bbanpattern !== "undefined") {
	// 	ibanregexp = new RegExp("^[A-Z]{2}\\d{2}" + bbanpattern + "$", "");
	// 	if (!(ibanregexp.test(iban))) {
	// 		return false; // Invalid country specific format
	// 	}
	// }

	// Now check the checksum, first convert to digits
	ibancheck := iban[4:len(iban)] + iban[0:4]
	lenCheck := len(ibancheck)
	leadingZeroes := true
	ibancheckdigits := ""

	for i := 0; i < lenCheck; i++ {
		character := ibancheck[i]
		if character != '0' {
			leadingZeroes = false
		}
		if !leadingZeroes {
			ibancheckdigits += strconv.Itoa(strings.Index("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", string(character)))
		}
	}

	// Calculate the result of: ibancheckdigits % 97
	cRest := 0
	lenD := len(ibancheckdigits)

	for p := 0; p < lenD; p++ {
		cChar := ibancheckdigits[p]
		cOperator, _ := strconv.Atoi("" + strconv.Itoa(cRest) + "" + string(cChar))
		cRest = cOperator % 97
	}

	return cRest == 1
}

// validateCIF checks the romanian id for company
func (guard SpecialGuard) validateCIF() bool {

	rawCif := guard.Value

	if lenght := len(rawCif); lenght > 10 || lenght < 6 {
		// log.Println("The length must be between 6 and 10 characters")
		return false
	}

	intCif, errInt := strconv.Atoi(rawCif)

	if errInt != nil {
		// log.Println("The CIF must contain only integers")
		return false
	}

	var (
		controlNumber = 753217532
		controlDigit1 = intCif % 10
		controlDigit2 = 0
	)

	// delete last digit
	intCif = intCif / 10

	t := 0

	for intCif > 0 {
		t += (intCif % 10) * (controlNumber % 10)

		intCif = intCif / 10
		controlNumber = controlNumber / 10
	}

	controlDigit2 = t * 10 % 11

	if controlDigit2 == 10 {
		controlDigit2 = 0
	}

	return controlDigit1 == controlDigit2
}

// validateCNP checks the romanian security id - CNP
func (guard SpecialGuard) validateCNP() bool {

	rawCNP := guard.Value

	if len(rawCNP) != 13 {
		// log.Println("The length of CNP is not 13 characters")
		return false
	}

	var (
		bigSum    int
		ctrlDigit int
		digits    = []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
		year      = 0
		control   = []int{2, 7, 9, 1, 4, 6, 3, 5, 8, 2, 7, 9}
	)

	for i := 0; i < 12; i++ {
		current, errCurrent := strconv.Atoi(string(rawCNP[i]))
		if errCurrent != nil {
			// log.Println("The character at position " + strconv.Itoa(i) + "[" + string(rawCNP[i]) + "] is not a digit")
			return false
		}
		bigSum += control[i] * current
		digits[i] = current
	}

	// check last digit
	_, errLastDigit := strconv.Atoi(string(rawCNP[12]))
	if errLastDigit != nil {
		// log.Println("The character at position " + strconv.Itoa(12) + "[" + string(rawCNP[12]) + "] is not a digit")
		return false
	}

	// Sex -  allowed only 1 -> 9
	if digits[0] == 0 {
		// log.Println("Sex can not be 0")
		return false
	}

	// year
	year = digits[1]*10 + digits[2]

	switch digits[0] {
	case 1, 2:
		year += 1900
		break
	case 3, 4:
		year += 1800
		break
	case 5, 6:
		year += 2000
		break
		// TODO to check
	case 7, 8, 9:
		year += 2000
		now := time.Now()
		if year > now.Year()-14 {
			year -= 100
		}
		break
	}

	if year < 1800 || year > 2099 {
		// log.Println("Wrong year: " + strconv.Itoa(year))
		return false
	}

	// Month - allowed only 1 -> 12
	month := digits[3]*10 + digits[4]
	if month < 1 || month > 12 {
		// log.Println("Wrong month: " + strconv.Itoa(month))
		return false
	}

	day := digits[5]*10 + digits[6]

	// check date
	t := time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)

	if int(t.Year()) != year || int(t.Month()) != month || t.Day() != day {
		// log.Println("The date does not exist: " + strconv.Itoa(year) + "/" + strconv.Itoa(month) + "/" + strconv.Itoa(day))
		return false
	}

	// County - allowed only 1 -> 52
	county := digits[7]*10 + digits[8]
	if county < 1 || county > 52 {
		// log.Println("Wrong county id: " + strconv.Itoa(county))
		return false
	}

	// Number - allowed only 001 --> 999
	number := digits[9]*100 + digits[10]*10 + digits[11]
	if number < 1 || number > 999 {
		// log.Println("Wrong number: " + strconv.Itoa(number))
		return false
	}

	// Check control
	ctrlDigit = bigSum % 11
	if ctrlDigit == 10 {
		ctrlDigit = 1
	}
	return strconv.Itoa(ctrlDigit) == string(rawCNP[12])
}

// validateShortDate validates a date in the format "02.01.2006"
func (guard SpecialGuard) validateShortDate() bool {
	dateFormat := "02.01.2006"
	_, err := time.Parse(dateFormat, guard.Value)
	return err == nil
}

// validateDate validates a date in the format "02.01.2006T15:04:05"
func (guard SpecialGuard) validateLongDate() bool {
	dateFormat := "02.01.2006T15:04:05"
	_, err := time.Parse(dateFormat, guard.Value)
	return err == nil
}

// validateEmail checks if the value is an email
func (guard SpecialGuard) validateEmail() bool {
	length := len([]rune(guard.Value))
	return (length >= 8 && length <= 60 && guard.checkRegexp("^.+\\@.+\\..+$"))
}
<commit_msg>change regex for email<commit_after>package validity

import (
	"errors"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// SpecialGuard is a validator for string types
type SpecialGuard struct {
	Value string
	Rules []string
}

// Check ensures that the value is ok
func (guard SpecialGuard) Check() Result {
	result := Result{
		Errors:  []string{},
		IsValid: true,
	}
	for _, rule := range guard.Rules {
		isValid, err := guard.checkRule(rule)
		if err != nil {
			panic(err)
		}
		if !isValid {
			result.Errors = append(result.Errors, "SPECIAL#"+rule)
			result.IsValid = false
		}
	}
	return result
}

func (guard SpecialGuard) checkRule(rule string) (bool, error) {
	switch rule {
	case SpecialIBAN:
		return guard.validateIBAN(), nil
	case SpecialCIF:
		return guard.validateCIF(), nil
	case SpecialCnp:
		return guard.validateCNP(), nil
	case SpecialShortDate:
		return guard.validateShortDate(), nil
	case SpecialLongDate:
		return guard.validateLongDate(), nil
	case SpecialEmail:
		return guard.validateEmail(), nil
	}
	return false, errors.New("The guardian SPECIAL does not have the rule [" + rule + "]")
}

func (guard SpecialGuard) toInt(s string) int {
	out, _ := strconv.ParseInt(s, 10, 64)

	return int(out)
}

func (guard SpecialGuard) checkRegexp(r string) bool {
	expression, _ := regexp.Compile(r)

	return expression.MatchString(guard.Value)
}

// validateIBAN validates a bank account
// It must NOT have whitespaces
func (guard SpecialGuard) validateIBAN() bool {

	iban := strings.ToUpper(guard.Value)

	if len(iban) < 10 {
		// log.Println("The IBAN must have at least 10 characters")
		return false
	}

	countrycode := iban[0:2]

	// Check the country code and find the country specific format
	bbancountrypatterns := map[string]string{
		// "AL": "\\d{8}[\\dA-Z]{16}",
		// "AD": "\\d{8}[\\dA-Z]{12}",
		// "AT": "\\d{16}",
		// "AZ": "[\\dA-Z]{4}\\d{20}",
		// "BE": "\\d{12}",
		// "BH": "[A-Z]{4}[\\dA-Z]{14}",
		// "BA": "\\d{16}",
		// "BR": "\\d{23}[A-Z][\\dA-Z]",
		// "BG": "[A-Z]{4}\\d{6}[\\dA-Z]{8}",
		// "CR": "\\d{17}",
		// "HR": "\\d{17}",
		// "CY": "\\d{8}[\\dA-Z]{16}",
		// "CZ": "\\d{20}",
		// "DK": "\\d{14}",
		// "DO": "[A-Z]{4}\\d{20}",
		// "EE": "\\d{16}",
		// "FO": "\\d{14}",
		// "FI": "\\d{14}",
		// "FR": "\\d{10}[\\dA-Z]{11}\\d{2}",
		// "GE": "[\\dA-Z]{2}\\d{16}",
		// "DE": "\\d{18}",
		// "GI": "[A-Z]{4}[\\dA-Z]{15}",
		// "GR": "\\d{7}[\\dA-Z]{16}",
		// "GL": "\\d{14}",
		// "GT": "[\\dA-Z]{4}[\\dA-Z]{20}",
		// "HU": "\\d{24}",
		// "IS": "\\d{22}",
		// "IE": "[\\dA-Z]{4}\\d{14}",
		// "IL": "\\d{19}",
		// "IT": "[A-Z]\\d{10}[\\dA-Z]{12}",
		// "KZ": "\\d{3}[\\dA-Z]{13}",
		// "KW": "[A-Z]{4}[\\dA-Z]{22}",
		// "LV": "[A-Z]{4}[\\dA-Z]{13}",
		// "LB": "\\d{4}[\\dA-Z]{20}",
		// "LI": "\\d{5}[\\dA-Z]{12}",
		// "LT": "\\d{16}",
		// "LU": "\\d{3}[\\dA-Z]{13}",
		// "MK": "\\d{3}[\\dA-Z]{10}\\d{2}",
		// "MT": "[A-Z]{4}\\d{5}[\\dA-Z]{18}",
		// "MR": "\\d{23}",
		// "MU": "[A-Z]{4}\\d{19}[A-Z]{3}",
		// "MC": "\\d{10}[\\dA-Z]{11}\\d{2}",
		// "MD": "[\\dA-Z]{2}\\d{18}",
		// "ME": "\\d{18}",
		// "NL": "[A-Z]{4}\\d{10}",
		// "NO": "\\d{11}",
		// "PK": "[\\dA-Z]{4}\\d{16}",
		// "PS": "[\\dA-Z]{4}\\d{21}",
		// "PL": "\\d{24}",
		// "PT": "\\d{21}",
		// "SM": "[A-Z]\\d{10}[\\dA-Z]{12}",
		// "SA": "\\d{2}[\\dA-Z]{18}",
		// "RS": "\\d{18}",
		// "SK": "\\d{20}",
		// "SI": "\\d{15}",
		// "ES": "\\d{20}",
		// "SE": "\\d{20}",
		// "CH": "\\d{5}[\\dA-Z]{12}",
		// "TN": "\\d{20}",
		// "TR": "\\d{5}[\\dA-Z]{17}",
		// "AE": "\\d{3}\\d{16}",
		// "GB": "[A-Z]{4}\\d{14}",
		// "VG": "[\\dA-Z]{4}\\d{16}",
		"RO": "[A-Z]{4}[\\dA-Z]{16}",
	}

	_, isInTheList := bbancountrypatterns[countrycode]

	if !isInTheList {
		// log.Println("The country code is not in the list")
		return false
	}

	// // As new countries will start using IBAN in the
	// // future, we only check if the countrycode is known.
	// // This prevents false negatives, while almost all
	// // false positives introduced by this, will be caught
	// // by the checksum validation below anyway.
	// // Strict checking should return FALSE for unknown
	// // countries.
	// if (typeof bbanpattern !== "undefined") {
	// 	ibanregexp = new RegExp("^[A-Z]{2}\\d{2}" + bbanpattern + "$", "");
	// 	if (!(ibanregexp.test(iban))) {
	// 		return false; // Invalid country specific format
	// 	}
	// }

	// Now check the checksum, first convert to digits
	ibancheck := iban[4:len(iban)] + iban[0:4]
	lenCheck := len(ibancheck)
	leadingZeroes := true
	ibancheckdigits := ""

	for i := 0; i < lenCheck; i++ {
		character := ibancheck[i]
		if character != '0' {
			leadingZeroes = false
		}
		if !leadingZeroes {
			ibancheckdigits += strconv.Itoa(strings.Index("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", string(character)))
		}
	}

	// Calculate the result of: ibancheckdigits % 97
	cRest := 0
	lenD := len(ibancheckdigits)

	for p := 0; p < lenD; p++ {
		cChar := ibancheckdigits[p]
		cOperator, _ := strconv.Atoi("" + strconv.Itoa(cRest) + "" + string(cChar))
		cRest = cOperator % 97
	}

	return cRest == 1
}

// validateCIF checks the romanian id for company
func (guard SpecialGuard) validateCIF() bool {

	rawCif := guard.Value

	if lenght := len(rawCif); lenght > 10 || lenght < 6 {
		// log.Println("The length must be between 6 and 10 characters")
		return false
	}

	intCif, errInt := strconv.Atoi(rawCif)

	if errInt != nil {
		// log.Println("The CIF must contain only integers")
		return false
	}

	var (
		controlNumber = 753217532
		controlDigit1 = intCif % 10
		controlDigit2 = 0
	)

	// delete last digit
	intCif = intCif / 10

	t := 0

	for intCif > 0 {
		t += (intCif % 10) * (controlNumber % 10)

		intCif = intCif / 10
		controlNumber = controlNumber / 10
	}

	controlDigit2 = t * 10 % 11

	if controlDigit2 == 10 {
		controlDigit2 = 0
	}

	return controlDigit1 == controlDigit2
}

// validateCNP checks the romanian security id - CNP
func (guard SpecialGuard) validateCNP() bool {

	rawCNP := guard.Value

	if len(rawCNP) != 13 {
		// log.Println("The length of CNP is not 13 characters")
		return false
	}

	var (
		bigSum    int
		ctrlDigit int
		digits    = []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
		year      = 0
		control   = []int{2, 7, 9, 1, 4, 6, 3, 5, 8, 2, 7, 9}
	)

	for i := 0; i < 12; i++ {
		current, errCurrent := strconv.Atoi(string(rawCNP[i]))
		if errCurrent != nil {
			// log.Println("The character at position " + strconv.Itoa(i) + "[" + string(rawCNP[i]) + "] is not a digit")
			return false
		}
		bigSum += control[i] * current
		digits[i] = current
	}

	// check last digit
	_, errLastDigit := strconv.Atoi(string(rawCNP[12]))
	if errLastDigit != nil {
		// log.Println("The character at position " + strconv.Itoa(12) + "[" + string(rawCNP[12]) + "] is not a digit")
		return false
	}

	// Sex -  allowed only 1 -> 9
	if digits[0] == 0 {
		// log.Println("Sex can not be 0")
		return false
	}

	// year
	year = digits[1]*10 + digits[2]

	switch digits[0] {
	case 1, 2:
		year += 1900
		break
	case 3, 4:
		year += 1800
		break
	case 5, 6:
		year += 2000
		break
		// TODO to check
	case 7, 8, 9:
		year += 2000
		now := time.Now()
		if year > now.Year()-14 {
			year -= 100
		}
		break
	}

	if year < 1800 || year > 2099 {
		// log.Println("Wrong year: " + strconv.Itoa(year))
		return false
	}

	// Month - allowed only 1 -> 12
	month := digits[3]*10 + digits[4]
	if month < 1 || month > 12 {
		// log.Println("Wrong month: " + strconv.Itoa(month))
		return false
	}

	day := digits[5]*10 + digits[6]

	// check date
	t := time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)

	if int(t.Year()) != year || int(t.Month()) != month || t.Day() != day {
		// log.Println("The date does not exist: " + strconv.Itoa(year) + "/" + strconv.Itoa(month) + "/" + strconv.Itoa(day))
		return false
	}

	// County - allowed only 1 -> 52
	county := digits[7]*10 + digits[8]
	if county < 1 || county > 52 {
		// log.Println("Wrong county id: " + strconv.Itoa(county))
		return false
	}

	// Number - allowed only 001 --> 999
	number := digits[9]*100 + digits[10]*10 + digits[11]
	if number < 1 || number > 999 {
		// log.Println("Wrong number: " + strconv.Itoa(number))
		return false
	}

	// Check control
	ctrlDigit = bigSum % 11
	if ctrlDigit == 10 {
		ctrlDigit = 1
	}
	return strconv.Itoa(ctrlDigit) == string(rawCNP[12])
}

// validateShortDate validates a date in the format "02.01.2006"
func (guard SpecialGuard) validateShortDate() bool {
	dateFormat := "02.01.2006"
	_, err := time.Parse(dateFormat, guard.Value)
	return err == nil
}

// validateDate validates a date in the format "02.01.2006T15:04:05"
func (guard SpecialGuard) validateLongDate() bool {
	dateFormat := "02.01.2006T15:04:05"
	_, err := time.Parse(dateFormat, guard.Value)
	return err == nil
}

// validateEmail checks if the value is an email
func (guard SpecialGuard) validateEmail() bool {
	length := len([]rune(guard.Value))
	isGoodFormat := func () bool  {
		reg := regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`)
 		return reg.MatchString(guard.Value)
	}
	return length >= 8 && length <= 60 && isGoodFormat()
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gogap/spirit"
	"io/ioutil"
	"os"
	"path"
	"strings"
	"text/template"
	"time"
)

var (
	ErrNoURNPackageSourceFound = errors.New("no urn packages source found")
	ErrConfigFileNameIsEmpty   = errors.New("config file name is empty")
)

type SpiritHelper struct {
	conf           spirit.SpiritConfig
	configFile     string
	configFileName string
	originalConfig []byte

	RefURNs     []string
	RefPackages []Package
}

func (p *SpiritHelper) LoadSpiritConfig(filename string) (err error) {

	if filename == "" {
		err = ErrConfigFileNameIsEmpty
		return
	}

	if fi, e := os.Stat(filename); e != nil {
		err = e
		return
	} else {
		p.configFile = filename
		p.configFileName = fi.Name()
	}

	if p.originalConfig, err = ioutil.ReadFile(filename); err != nil {
		return
	}

	if err = json.Unmarshal(p.originalConfig, &p.conf); err != nil {
		return
	}

	return
}

func (p *SpiritHelper) CreateProject(createOpts CreateOptions, tmplArgs map[string]interface{}) (err error) {
	if err = createOpts.Validate(); err != nil {
		return
	}

	goSrc := path.Join(createOpts.GoPath, "src")

	if err = p.parse(goSrc, createOpts.Sources); err != nil {
		return
	}

	// download packages
	if createOpts.GetPackages {
		if err = p.GetPackages(createOpts.PackagesRevision, createOpts.UpdatePackages); err != nil {
			return
		}
	}

	// make project dir
	projectPath := path.Join(goSrc, createOpts.ProjectPath)
	if path.IsAbs(createOpts.ProjectPath) {
		projectPath = createOpts.ProjectPath
	}

	if fi, e := os.Stat(projectPath); e != nil {
		if !strings.Contains(e.Error(), "no such file or directory") &&
			!os.IsNotExist(e) {
			err = e
			return
		}
	} else if !fi.IsDir() {
		err = fmt.Errorf("your project path %s already exist, but it is not a directory", projectPath)
		return
	} else if createOpts.ForceWrite {
		spirit.Logger().Warnf("project path %s already exist, it will be overwrite", projectPath)
	} else {
		err = fmt.Errorf("your project path %s already exist", projectPath)
		return
	}

	if err = os.MkdirAll(projectPath, os.FileMode(0755)); err != nil {
		if !os.IsNotExist(err) {
			return
		} else if !createOpts.ForceWrite {
			return
		}
		err = nil
	}

	// render code template
	tmplPathFmt := "github.com/gogap/spirit-tool/template/%s/main.go"
	tmplArgsPathFmt := "github.com/gogap/spirit-tool/template/%s/args.json"

	tmplPath := path.Join(goSrc, fmt.Sprintf(tmplPathFmt, createOpts.TemplateName))
	spirit.Logger().Infof("using template of %s: %s", createOpts.TemplateName, tmplPath)

	tmplArgsPath := path.Join(goSrc, fmt.Sprintf(tmplArgsPathFmt, createOpts.TemplateName))
	spirit.Logger().Infof("using template args of %s: %s", createOpts.TemplateName, tmplArgsPath)

	var tmpl *template.Template
	if tmpl, err = template.New("main.go").Option("missingkey=error").Delims("//<-", "->//").ParseFiles(tmplPath); err != nil {
		return
	}

	internalArgs := map[string]interface{}{}
	if argData, e := ioutil.ReadFile(tmplArgsPath); e == nil {
		if err = json.Unmarshal(argData, &internalArgs); err != nil {
			return
		}
	}

	if tmplArgs != nil {
		for k, v := range tmplArgs {
			internalArgs[k] = v
		}
	}

	buffer := &bytes.Buffer{}
	if err = tmpl.Execute(buffer, map[string]interface{}{
		"create_options":  createOpts,
		"packages":        p.RefPackages,
		"config":          p.configFile,
		"config_filename": p.configFileName,
		"create_time":     time.Now(),
		"args":            internalArgs}); err != nil {
		return
	}

	srcPath := path.Join(projectPath, "main.go")
	if err = ioutil.WriteFile(srcPath, buffer.Bytes(), os.FileMode(0644)); err != nil {
		return
	}

	confPath := path.Join(projectPath, p.configFileName)
	if err = ioutil.WriteFile(confPath, p.originalConfig, os.FileMode(0644)); err != nil {
		return
	}

	// format code for sort import packages order
	if _, err = execCommand("go fmt " + srcPath); err != nil {
		return
	}

	spirit.Logger().Infof("project created at %s\n", projectPath)

	return
}

func (p *SpiritHelper) GetPackages(pkgRevision map[string]string, update bool) (err error) {
	for _, pkg := range p.RefPackages {
		if pkgRevision != nil {
			if revision, exist := pkgRevision[pkg.URI]; exist {
				pkg.Revision = revision
			}
		}
		if err = pkg.Get(update); err != nil {
			return
		}
	}
	return
}

func (p *SpiritHelper) RunProject(createOpts CreateOptions, tmplArgs map[string]interface{}) (err error) {

	if err = p.CreateProject(createOpts, tmplArgs); err != nil {
		return
	}

	if _, err = execCommandWithDir("go build -o main "+path.Join(createOpts.ProjectPath, "main.go"), createOpts.ProjectPath); err != nil {
		return
	}

	if cmder, e := execute(path.Join(createOpts.ProjectPath, "main"), createOpts.ProjectPath); e != nil {
		err = e
		return
	} else {
		cmder.Wait()
	}

	return
}

func (p *SpiritHelper) parse(gosrc string, sources []string) (err error) {
	if sources == nil || len(sources) == 0 {
		err = ErrNoURNPackageSourceFound
		return
	}

	var urns []string

	if urns = parseActorsUsingURN(
		p.conf.InputTranslators,
		p.conf.OutputTranslators,
		p.conf.Inboxes,
		p.conf.Outboxes,
		p.conf.Receivers,
		p.conf.Senders,
		p.conf.Routers,
		p.conf.Components,
		p.conf.LabelMatchers,
		p.conf.URNRewriters,
	); err != nil {
		return
	}

	for _, readerPool := range p.conf.ReaderPools {
		urns = append(urns, parseActorUsingURN(readerPool.ActorConfig)...)
		if readerPool.Reader != nil {
			urns = append(urns, parseActorUsingURN(*readerPool.Reader)...)
		}
	}

	for _, writerPool := range p.conf.WriterPools {
		urns = append(urns, parseActorUsingURN(writerPool.ActorConfig)...)
		if writerPool.Writer != nil {
			urns = append(urns, parseActorUsingURN(*writerPool.Writer)...)
		}
	}

	p.RefURNs = urns

	if p.RefPackages, err = urnsToPackages(gosrc, urns, sources...); err != nil {
		return
	}

	return
}

func parseActorsUsingURN(confs ...[]spirit.ActorConfig) (urns []string) {
	for _, conf := range confs {
		for _, c := range conf {
			urns = append(urns, c.URN)
		}
	}
	return
}

func parseActorUsingURN(actorConfs ...spirit.ActorConfig) (urns []string) {
	for _, conf := range actorConfs {
		urns = append(urns, conf.URN)
	}
	return
}

func urnsToPackages(gosrc string, urns []string, sourceFiles ...string) (packages []Package, err error) {
	urnPkgMap := map[string]string{}

	for _, sourceFile := range sourceFiles {
		var data []byte

		if data, err = ioutil.ReadFile(sourceFile); err != nil {
			return
		}

		sourceConf := SourceConfig{}
		if err = json.Unmarshal(data, &sourceConf); err != nil {
			return
		}

		for _, urnPkg := range sourceConf.Packages {
			if oldVal, exist := urnPkgMap[urnPkg.URN]; exist {
				if oldVal != urnPkg.Pkg {
					err = fmt.Errorf("source have duplicate urn pkg, urn:%s, pkg1:%s, pkg2: %s, file: %s", urnPkg.URN, oldVal, urnPkg.Pkg, sourceFile)
					return
				}
			}
			urnPkgMap[urnPkg.URN] = urnPkg.Pkg
		}
	}

	pkgs := map[string]bool{}

	for _, urn := range urns {
		if pkg, exist := urnPkgMap[urn]; !exist {
			err = fmt.Errorf("urn of %s not exist", urn)
		} else {
			pkgs[pkg] = true
		}
	}

	for pkg, _ := range pkgs {
		packages = append(packages, Package{gosrc: gosrc, URI: pkg, Revision: ""})
	}

	return
}
<commit_msg>improve promot of non urn package exist<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gogap/spirit"
	"io/ioutil"
	"os"
	"path"
	"strings"
	"text/template"
	"time"
)

var (
	ErrNoURNPackageSourceFound = errors.New("no urn packages source found")
	ErrConfigFileNameIsEmpty   = errors.New("config file name is empty")
)

type SpiritHelper struct {
	conf           spirit.SpiritConfig
	configFile     string
	configFileName string
	originalConfig []byte

	RefURNs     []string
	RefPackages []Package
}

func (p *SpiritHelper) LoadSpiritConfig(filename string) (err error) {

	if filename == "" {
		err = ErrConfigFileNameIsEmpty
		return
	}

	if fi, e := os.Stat(filename); e != nil {
		err = e
		return
	} else {
		p.configFile = filename
		p.configFileName = fi.Name()
	}

	if p.originalConfig, err = ioutil.ReadFile(filename); err != nil {
		return
	}

	if err = json.Unmarshal(p.originalConfig, &p.conf); err != nil {
		return
	}

	return
}

func (p *SpiritHelper) CreateProject(createOpts CreateOptions, tmplArgs map[string]interface{}) (err error) {
	if err = createOpts.Validate(); err != nil {
		return
	}

	goSrc := path.Join(createOpts.GoPath, "src")

	if err = p.parse(goSrc, createOpts.Sources); err != nil {
		return
	}

	// download packages
	if createOpts.GetPackages {
		if err = p.GetPackages(createOpts.PackagesRevision, createOpts.UpdatePackages); err != nil {
			return
		}
	}

	// make project dir
	projectPath := path.Join(goSrc, createOpts.ProjectPath)
	if path.IsAbs(createOpts.ProjectPath) {
		projectPath = createOpts.ProjectPath
	}

	if fi, e := os.Stat(projectPath); e != nil {
		if !strings.Contains(e.Error(), "no such file or directory") &&
			!os.IsNotExist(e) {
			err = e
			return
		}
	} else if !fi.IsDir() {
		err = fmt.Errorf("your project path %s already exist, but it is not a directory", projectPath)
		return
	} else if createOpts.ForceWrite {
		spirit.Logger().Warnf("project path %s already exist, it will be overwrite", projectPath)
	} else {
		err = fmt.Errorf("your project path %s already exist", projectPath)
		return
	}

	if err = os.MkdirAll(projectPath, os.FileMode(0755)); err != nil {
		if !os.IsNotExist(err) {
			return
		} else if !createOpts.ForceWrite {
			return
		}
		err = nil
	}

	// render code template
	tmplPathFmt := "github.com/gogap/spirit-tool/template/%s/main.go"
	tmplArgsPathFmt := "github.com/gogap/spirit-tool/template/%s/args.json"

	tmplPath := path.Join(goSrc, fmt.Sprintf(tmplPathFmt, createOpts.TemplateName))
	spirit.Logger().Infof("using template of %s: %s", createOpts.TemplateName, tmplPath)

	tmplArgsPath := path.Join(goSrc, fmt.Sprintf(tmplArgsPathFmt, createOpts.TemplateName))
	spirit.Logger().Infof("using template args of %s: %s", createOpts.TemplateName, tmplArgsPath)

	var tmpl *template.Template
	if tmpl, err = template.New("main.go").Option("missingkey=error").Delims("//<-", "->//").ParseFiles(tmplPath); err != nil {
		return
	}

	internalArgs := map[string]interface{}{}
	if argData, e := ioutil.ReadFile(tmplArgsPath); e == nil {
		if err = json.Unmarshal(argData, &internalArgs); err != nil {
			return
		}
	}

	if tmplArgs != nil {
		for k, v := range tmplArgs {
			internalArgs[k] = v
		}
	}

	buffer := &bytes.Buffer{}
	if err = tmpl.Execute(buffer, map[string]interface{}{
		"create_options":  createOpts,
		"packages":        p.RefPackages,
		"config":          p.configFile,
		"config_filename": p.configFileName,
		"create_time":     time.Now(),
		"args":            internalArgs}); err != nil {
		return
	}

	srcPath := path.Join(projectPath, "main.go")
	if err = ioutil.WriteFile(srcPath, buffer.Bytes(), os.FileMode(0644)); err != nil {
		return
	}

	confPath := path.Join(projectPath, p.configFileName)
	if err = ioutil.WriteFile(confPath, p.originalConfig, os.FileMode(0644)); err != nil {
		return
	}

	// format code for sort import packages order
	if _, err = execCommand("go fmt " + srcPath); err != nil {
		return
	}

	spirit.Logger().Infof("project created at %s\n", projectPath)

	return
}

func (p *SpiritHelper) GetPackages(pkgRevision map[string]string, update bool) (err error) {
	for _, pkg := range p.RefPackages {
		if pkgRevision != nil {
			if revision, exist := pkgRevision[pkg.URI]; exist {
				pkg.Revision = revision
			}
		}
		if err = pkg.Get(update); err != nil {
			return
		}
	}
	return
}

func (p *SpiritHelper) RunProject(createOpts CreateOptions, tmplArgs map[string]interface{}) (err error) {

	if err = p.CreateProject(createOpts, tmplArgs); err != nil {
		return
	}

	if _, err = execCommandWithDir("go build -o main "+path.Join(createOpts.ProjectPath, "main.go"), createOpts.ProjectPath); err != nil {
		return
	}

	if cmder, e := execute(path.Join(createOpts.ProjectPath, "main"), createOpts.ProjectPath); e != nil {
		err = e
		return
	} else {
		cmder.Wait()
	}

	return
}

func (p *SpiritHelper) parse(gosrc string, sources []string) (err error) {
	if sources == nil || len(sources) == 0 {
		err = ErrNoURNPackageSourceFound
		return
	}

	var urns []string

	if urns = parseActorsUsingURN(
		p.conf.InputTranslators,
		p.conf.OutputTranslators,
		p.conf.Inboxes,
		p.conf.Outboxes,
		p.conf.Receivers,
		p.conf.Senders,
		p.conf.Routers,
		p.conf.Components,
		p.conf.LabelMatchers,
		p.conf.URNRewriters,
	); err != nil {
		return
	}

	for _, readerPool := range p.conf.ReaderPools {
		urns = append(urns, parseActorUsingURN(readerPool.ActorConfig)...)
		if readerPool.Reader != nil {
			urns = append(urns, parseActorUsingURN(*readerPool.Reader)...)
		}
	}

	for _, writerPool := range p.conf.WriterPools {
		urns = append(urns, parseActorUsingURN(writerPool.ActorConfig)...)
		if writerPool.Writer != nil {
			urns = append(urns, parseActorUsingURN(*writerPool.Writer)...)
		}
	}

	p.RefURNs = urns

	if p.RefPackages, err = urnsToPackages(gosrc, urns, sources...); err != nil {
		return
	}

	return
}

func parseActorsUsingURN(confs ...[]spirit.ActorConfig) (urns []string) {
	for _, conf := range confs {
		for _, c := range conf {
			urns = append(urns, c.URN)
		}
	}
	return
}

func parseActorUsingURN(actorConfs ...spirit.ActorConfig) (urns []string) {
	for _, conf := range actorConfs {
		urns = append(urns, conf.URN)
	}
	return
}

func urnsToPackages(gosrc string, urns []string, sourceFiles ...string) (packages []Package, err error) {
	urnPkgMap := map[string]string{}

	for _, sourceFile := range sourceFiles {
		var data []byte

		if data, err = ioutil.ReadFile(sourceFile); err != nil {
			return
		}

		sourceConf := SourceConfig{}
		if err = json.Unmarshal(data, &sourceConf); err != nil {
			return
		}

		for _, urnPkg := range sourceConf.Packages {
			if oldVal, exist := urnPkgMap[urnPkg.URN]; exist {
				if oldVal != urnPkg.Pkg {
					err = fmt.Errorf("source have duplicate urn pkg, urn:%s, pkg1:%s, pkg2: %s, file: %s", urnPkg.URN, oldVal, urnPkg.Pkg, sourceFile)
					return
				}
			}
			urnPkgMap[urnPkg.URN] = urnPkg.Pkg
		}
	}

	pkgs := map[string]bool{}

	for _, urn := range urns {
		if pkg, exist := urnPkgMap[urn]; !exist {
			err = fmt.Errorf("no package from any source of urn: %s", urn)
			return
		} else {
			pkgs[pkg] = true
		}
	}

	for pkg, _ := range pkgs {
		packages = append(packages, Package{gosrc: gosrc, URI: pkg, Revision: ""})
	}

	return
}
<|endoftext|>


<commit_before>// hapi provides a Hypermedia API (aka "true" REST) micro-framework/toolkit
package hapi

// hapi uses httprouter (github.com/julienschmidt/httprouter) for HTTP routing, and exposes
// all of the httprouter functionality through hapi.Router:

// func main() {
//     hapi := hapi.New()
//     hapi.GET(...) // hapi version
//     hapi.Router.GET(...) // Underlying httprouter version
// }

import (
    "fmt"
    "log"
    "strings"

    "net/http"
    "github.com/julienschmidt/httprouter"   /* HTTP router */
    "bitbucket.org/ww/goautoneg"            /* To parse Accept: headers */
)

type Handle func(*Context)

type HypermediaAPI struct {
    Router          *httprouter.Router
    typeHandlers    map[string]map[string]Handle
}

type Context struct {
    Writer              http.ResponseWriter
    Request             *http.Request
    Params              httprouter.Params
    NegotiatedType      string
    Stash               map[string]interface{}
}

func New() *HypermediaAPI {
    return &HypermediaAPI{
        httprouter.New(),
        make(map[string]map[string]Handle),
    }
}

func (h *HypermediaAPI) GETAll(path string, handle Handle) {
    h.Register("GET",path,"*/*",handle)
}

func (h *HypermediaAPI) GET(path, ctype string, handle Handle) {
    h.Register("GET",path, ctype, handle)
}

func (h *HypermediaAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    h.Router.ServeHTTP(w,r)
}

func (h *HypermediaAPI) Register(method, path, ctype string, handle Handle) {
    key := method + " " + path
    ctypes := strings.Split(ctype," ")
    typeHandlers, registered := h.typeHandlers[key]
    for _,t := range ctypes {
        if _, ok := typeHandlers[t]; ok {
            panic(fmt.Sprintf("a handle is already registered for method %s, path '%s', type %s",method,path,ctype))
        }
    }

    if !registered {
        wrapper := func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
            negotiatedType, typeHandler := TypeNegotiator(r.Header.Get("Accept"), h.typeHandlers[key])
log.Printf("Accept: %s\n", r.Header.Get("Accept"))
            if len(negotiatedType) == 0 {
                log.Printf("We can't serve the requested type(s): %s\n", r.Header.Get("Accept"))
                // Fall back to unsupported type
            }
//            w.Header.Set("Content-Type", negotiatedType)
            context := &Context{
                w,
                r,
                p,
                negotiatedType,
                make(map[string]interface{}),
            }
            typeHandler( context )
            fmt.Fprintf(w, "%v", context)
            return
        }
        h.Router.Handle(method, path, wrapper)
        h.typeHandlers[key] = make(map[string]Handle)
    }
    for _,t := range ctypes {
        log.Printf("Registering for %s\n", t)
        h.typeHandlers[key][t] = handle
    }
}

func TypeNegotiator(acceptHeader string, typeHandlers map[string]Handle) (negotiatedType string, typeHandler Handle) {
    availableTypes := make([]string,len(typeHandlers))
    i := 0
    for k,_ := range typeHandlers {
        availableTypes[i] = k
        i++
    }
    if len(acceptHeader) == 0 {
        acceptHeader = "*/*"
    }
    negotiatedType = Negotiate(acceptHeader,availableTypes)
    if len(negotiatedType) == 0 {
        // This means we can't serve the requested type
        return
    }
    for _,negType := range []string{ negotiatedType, negotiatedType[0:strings.Index(negotiatedType,"/")]+"/*", "*/*" } {
        if handler,ok := typeHandlers[negType]; ok {
            typeHandler = handler
            return
        }
    }
    typeHandler = nil
    return
}

// Borrowed from goautoneg, and adapted
func Negotiate(header string, alternatives []string) (content_type string) {
    asp := make([][]string, 0, len(alternatives))
    for _, ctype := range alternatives {
        asp = append(asp, strings.SplitN(ctype, "/", 2))
    }
    for _, clause := range goautoneg.ParseAccept(header) {
        for i, ctsp := range asp {
            if clause.Type == ctsp[0] && clause.SubType == ctsp[1] {
                content_type = alternatives[i]
                return
            }
            if clause.Type == ctsp[0] && clause.SubType == "*" {
                content_type = alternatives[i]
                return
            }
            if clause.Type == "*" && clause.SubType == "*" {
                content_type = alternatives[i]
                return
            }
            if clause.Type == ctsp[0] && ctsp[1] == "*" {
                content_type = clause.Type + "/" + clause.SubType
                return
            }
            if ctsp[0] == "*" && ctsp[1] == "*" {
                content_type = clause.Type + "/" + clause.SubType
                return
            }
        }
    }
    return
}<commit_msg>Add some documentatoin for hapi.Register()<commit_after>// hapi provides a Hypermedia API (aka "true" REST) micro-framework/toolkit
package hapi

// hapi uses httprouter (github.com/julienschmidt/httprouter) for HTTP routing, and exposes
// all of the httprouter functionality through hapi.Router:

// func main() {
//     hapi := hapi.New()
//     hapi.GET(...) // hapi version
//     hapi.Router.GET(...) // Underlying httprouter version
// }

import (
    "fmt"
    "log"
    "strings"

    "net/http"
    "github.com/julienschmidt/httprouter"   /* HTTP router */
    "bitbucket.org/ww/goautoneg"            /* To parse Accept: headers */
)

type Handle func(*Context)

type HypermediaAPI struct {
    Router          *httprouter.Router
    typeHandlers    map[string]map[string]Handle
}

type Context struct {
    Writer              http.ResponseWriter
    Request             *http.Request
    Params              httprouter.Params
    NegotiatedType      string
    Stash               map[string]interface{}
}

func New() *HypermediaAPI {
    return &HypermediaAPI{
        httprouter.New(),
        make(map[string]map[string]Handle),
    }
}

func (h *HypermediaAPI) GETAll(path string, handle Handle) {
    h.Register("GET",path,"*/*",handle)
}

func (h *HypermediaAPI) GET(path, ctype string, handle Handle) {
    h.Register("GET",path, ctype, handle)
}

func (h *HypermediaAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    h.Router.ServeHTTP(w,r)
}

// Register() registers a handler method to handle a specific Method/path/content-type combination
// Method and path ought to be self-explanatory.
// The content type argument should be a space-separated list of valid content types. For the moment
// all parameters are ignored, but I hope to implement support for charset eventually
// The media type may be specified as '*/*' to act as a catch-all. No other wildcards (e.g. 'text/*') are permitted
func (h *HypermediaAPI) Register(method, path, ctype string, handle Handle) {
    key := method + " " + path
    ctypes := strings.Split(ctype," ")
    if typeHandlers, registered := h.typeHandlers[key]; registered {
        for _,t := range ctypes {
            if _, ok := typeHandlers[t]; ok {
                panic(fmt.Sprintf("a handle is already registered for method %s, path '%s', type %s",method,path,ctype))
            }
        }
    } else {
        wrapper := func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
            negotiatedType, typeHandler := TypeNegotiator(r.Header.Get("Accept"), h.typeHandlers[key])
log.Printf("Accept: %s\n", r.Header.Get("Accept"))
            if len(negotiatedType) == 0 {
                log.Printf("We can't serve the requested type(s): %s\n", r.Header.Get("Accept"))
                // Fall back to unsupported type
            }
//            w.Header.Set("Content-Type", negotiatedType)
            context := &Context{
                w,
                r,
                p,
                negotiatedType,
                make(map[string]interface{}),
            }
            typeHandler( context )
            fmt.Fprintf(w, "%v", context)
            return
        }
        h.Router.Handle(method, path, wrapper)
        h.typeHandlers[key] = make(map[string]Handle)
    }
    for _,t := range ctypes {
        log.Printf("Registering for %s\n", t)
        h.typeHandlers[key][t] = handle
    }
}

func TypeNegotiator(acceptHeader string, typeHandlers map[string]Handle) (negotiatedType string, typeHandler Handle) {
    availableTypes := make([]string,len(typeHandlers))
    i := 0
    for k,_ := range typeHandlers {
        availableTypes[i] = k
        i++
    }
    if len(acceptHeader) == 0 {
        acceptHeader = "*/*"
    }
    negotiatedType = Negotiate(acceptHeader,availableTypes)
    if len(negotiatedType) == 0 {
        // This means we can't serve the requested type
        return
    }
    for _,negType := range []string{ negotiatedType, negotiatedType[0:strings.Index(negotiatedType,"/")]+"/*", "*/*" } {
        if handler,ok := typeHandlers[negType]; ok {
            typeHandler = handler
            return
        }
    }
    typeHandler = nil
    return
}

// Borrowed from goautoneg, and adapted
func Negotiate(header string, alternatives []string) (content_type string) {
    asp := make([][]string, 0, len(alternatives))
    for _, ctype := range alternatives {
        asp = append(asp, strings.SplitN(ctype, "/", 2))
    }
    for _, clause := range goautoneg.ParseAccept(header) {
        for i, ctsp := range asp {
            if clause.Type == ctsp[0] && clause.SubType == ctsp[1] {
                content_type = alternatives[i]
                return
            }
            if clause.Type == ctsp[0] && clause.SubType == "*" {
                content_type = alternatives[i]
                return
            }
            if clause.Type == "*" && clause.SubType == "*" {
                content_type = alternatives[i]
                return
            }
            if clause.Type == ctsp[0] && ctsp[1] == "*" {
                content_type = clause.Type + "/" + clause.SubType
                return
            }
            if ctsp[0] == "*" && ctsp[1] == "*" {
                content_type = clause.Type + "/" + clause.SubType
                return
            }
        }
    }
    return
}<|endoftext|>


<commit_before>package main

import (
	"compress/gzip"
	"encoding/base32"
	"encoding/binary"
	"encoding/gob"
	"log"
	"os"
	"path/filepath"
	"sync"
	"time"
)

type Player struct {
	ID uint64
	Hero
	ZoneX, ZoneY int64
	TileX, TileY uint8
	hud          interface {
		Paint(func(int, int, rune, Color))
		Key(int) bool
	}
	repaint chan struct{}

	Joined    time.Time
	LastLogin time.Time
	Admin     bool

	zone *Zone
}

func (p *Player) Move(dx, dy int) {
	if p.Delay > 0 {
		return
	}
	destX := dx + int(p.TileX)
	destY := dy + int(p.TileY)

	zoneChange := destX < 0 || destY < 0 || destX > 255 || destY > 255

	p.lock.Lock()
	z := p.zone
	p.lock.Unlock()
	z.Lock()
	if !zoneChange {
		zoneChange = z.Tile(uint8(destX), uint8(destY)) == nil
	}
	if !zoneChange && z.Blocked(uint8(destX), uint8(destY)) {
		z.Unlock()
		return
	}
	z.Tile(p.TileX, p.TileY).Remove(p)
	z.Unlock()
	if zoneChange {
		p.RepaintZone()
		ReleaseZone(z)
		p.lock.Lock()
		p.Delay = 2
		if destY < 0 {
			p.ZoneY -= 2
			p.TileX = 127
			p.TileY = 255
		} else if destY > 255 {
			p.ZoneY += 2
			p.TileX = 127
			p.TileY = 0
		} else if destX < 128 {
			if destY < 128 {
				p.ZoneX--
				p.ZoneY--
				p.TileX = 255 - zoneOffset[255-64]
				p.TileY = 255 - 64
			} else {
				p.ZoneX--
				p.ZoneY++
				p.TileX = 255 - zoneOffset[64]
				p.TileY = 64
			}
		} else {
			if destY < 128 {
				p.ZoneY--
				p.TileX = zoneOffset[255-64]
				p.TileY = 255 - 64
			} else {
				p.ZoneY++
				p.TileX = zoneOffset[64]
				p.TileY = 64
			}
		}
		z = GrabZone(p.ZoneX, p.ZoneY)
		p.zone = z
		p.lock.Unlock()
		p.Save()
		p.hud = nil
	} else {
		p.lock.Lock()
		p.TileX = uint8(destX)
		p.TileY = uint8(destY)
		p.Delay = 2
		p.lock.Unlock()
	}
	z.Lock()
	z.Tile(p.TileX, p.TileY).Add(p)
	z.Unlock()
	p.RepaintZone()
}

func playerFilename(id uint64) string {
	var buf [binary.MaxVarintLen64]byte
	i := binary.PutUvarint(buf[:], id)
	encoded := base32.StdEncoding.EncodeToString(buf[:i])

	l := len(encoded)
	for encoded[l-1] == '=' {
		l--
	}
	return "p" + encoded[:l] + ".gz"
}

func (p *Player) Save() {
	p.lock.Lock()
	defer p.lock.Unlock()

	dir := seedFilename()
	os.MkdirAll(dir, 0755)

	f, err := os.Create(filepath.Join(dir, playerFilename(p.ID)))
	if err != nil {
		log.Printf("[save:%d] %v", p.ID, err)
		return
	}
	defer f.Close()

	g, err := gzip.NewWriterLevel(f, gzip.BestCompression)
	if err != nil {
		log.Printf("[save:%d] %v", p.ID, err)
		return
	}
	defer g.Close()

	err = gob.NewEncoder(g).Encode(p)
	if err != nil {
		log.Printf("[save:%d] %v", p.ID, err)
	}
}

func LoadPlayer(id uint64) (*Player, error) {
	dir := seedFilename()

	f, err := os.Open(filepath.Join(dir, playerFilename(id)))
	if err != nil {
		return nil, err
	}
	defer f.Close()

	g, err := gzip.NewReader(f)
	if err != nil {
		return nil, err
	}
	defer g.Close()

	d := gob.NewDecoder(g)
	var p Player
	err = d.Decode(&p)
	if err != nil {
		return nil, err
	}
	p.repaint = make(chan struct{}, 1)
	return &p, nil
}

func (p *Player) Repaint() {
	select {
	case p.repaint <- struct{}{}:
	default:
	}
}

func (p *Player) RepaintZone() {
	go func() {
		p.lock.Lock()
		p.zone.Repaint()
		p.lock.Unlock()
	}()
}

func (p *Player) Paint() (rune, Color) {
	if p.Admin {
		return '♚', "#fa0"
	}
	return p.Hero.Paint()
}

func (p *Player) Think() {
	p.think(false)
}

type ZoneEntryHUD string

func (h ZoneEntryHUD) Paint(setcell func(int, int, rune, Color)) {
	i := 0
	for _, r := range h {
		setcell(i, 0, r, "#fff")
		i++
	}
}

func (h ZoneEntryHUD) Key(code int) bool {
	return false
}

type Hero struct {
	Name_ *Name

	lock     sync.Mutex
	Delay    uint
	Backpack []Object
}

func (h *Hero) Name() string {
	return h.Name_.String()
}

func (h *Hero) Examine() string {
	return "a hero."
}

func (h *Hero) Blocking() bool {
	return false
}

func (h *Hero) Paint() (rune, Color) {
	return '☻', "#fff"
}

func (h *Hero) Think() {
	h.think(true)
}

func (h *Hero) think(ai bool) {
	h.lock.Lock()
	defer h.lock.Unlock()

	if h.Delay > 0 {
		h.Delay--
		return
	}
}

func (h *Hero) InteractOptions() []string {
	return nil
}

func (h *Hero) GiveItem(o Object) {
	h.Backpack = append(h.Backpack, o)
}
<commit_msg>actually fix the problem instead of making it worse<commit_after>package main

import (
	"compress/gzip"
	"encoding/base32"
	"encoding/binary"
	"encoding/gob"
	"log"
	"os"
	"path/filepath"
	"sync"
	"time"
)

type Player struct {
	ID uint64
	Hero
	ZoneX, ZoneY int64
	TileX, TileY uint8
	hud          interface {
		Paint(func(int, int, rune, Color))
		Key(int) bool
	}
	repaint chan struct{}

	Joined    time.Time
	LastLogin time.Time
	Admin     bool

	zone *Zone
}

func (p *Player) Move(dx, dy int) {
	if p.Delay > 0 {
		return
	}
	destX := dx + int(p.TileX)
	destY := dy + int(p.TileY)

	zoneChange := destX < 0 || destY < 0 || destX > 255 || destY > 255

	p.lock.Lock()
	z := p.zone
	p.lock.Unlock()
	z.Lock()
	if !zoneChange {
		zoneChange = z.Tile(uint8(destX), uint8(destY)) == nil
	}
	if !zoneChange && z.Blocked(uint8(destX), uint8(destY)) {
		z.Unlock()
		return
	}
	z.Tile(p.TileX, p.TileY).Remove(p)
	z.Unlock()
	if zoneChange {
		p.RepaintZone()
		ReleaseZone(z)
		p.lock.Lock()
		p.Delay = 2
		if destY < 0 {
			p.ZoneY -= 2
			p.TileX = 127
			p.TileY = 255
		} else if destY > 255 {
			p.ZoneY += 2
			p.TileX = 127
			p.TileY = 0
		} else if destX < 128 {
			if p.ZoneY & 1 == 1 {
				p.ZoneX--
			}
			if destY < 128 {
				p.ZoneY--
				p.TileX = 255 - zoneOffset[255-64]
				p.TileY = 255 - 64
			} else {
				p.ZoneY++
				p.TileX = 255 - zoneOffset[64]
				p.TileY = 64
			}
		} else {
			if p.ZoneY & 1 == 1 {
				p.ZoneX++
			}
			if destY < 128 {
				p.ZoneY--
				p.TileX = zoneOffset[255-64]
				p.TileY = 255 - 64
			} else {
				p.ZoneY++
				p.TileX = zoneOffset[64]
				p.TileY = 64
			}
		}
		z = GrabZone(p.ZoneX, p.ZoneY)
		p.zone = z
		p.lock.Unlock()
		p.Save()
		p.hud = nil
	} else {
		p.lock.Lock()
		p.TileX = uint8(destX)
		p.TileY = uint8(destY)
		p.Delay = 2
		p.lock.Unlock()
	}
	z.Lock()
	z.Tile(p.TileX, p.TileY).Add(p)
	z.Unlock()
	p.RepaintZone()
}

func playerFilename(id uint64) string {
	var buf [binary.MaxVarintLen64]byte
	i := binary.PutUvarint(buf[:], id)
	encoded := base32.StdEncoding.EncodeToString(buf[:i])

	l := len(encoded)
	for encoded[l-1] == '=' {
		l--
	}
	return "p" + encoded[:l] + ".gz"
}

func (p *Player) Save() {
	p.lock.Lock()
	defer p.lock.Unlock()

	dir := seedFilename()
	os.MkdirAll(dir, 0755)

	f, err := os.Create(filepath.Join(dir, playerFilename(p.ID)))
	if err != nil {
		log.Printf("[save:%d] %v", p.ID, err)
		return
	}
	defer f.Close()

	g, err := gzip.NewWriterLevel(f, gzip.BestCompression)
	if err != nil {
		log.Printf("[save:%d] %v", p.ID, err)
		return
	}
	defer g.Close()

	err = gob.NewEncoder(g).Encode(p)
	if err != nil {
		log.Printf("[save:%d] %v", p.ID, err)
	}
}

func LoadPlayer(id uint64) (*Player, error) {
	dir := seedFilename()

	f, err := os.Open(filepath.Join(dir, playerFilename(id)))
	if err != nil {
		return nil, err
	}
	defer f.Close()

	g, err := gzip.NewReader(f)
	if err != nil {
		return nil, err
	}
	defer g.Close()

	d := gob.NewDecoder(g)
	var p Player
	err = d.Decode(&p)
	if err != nil {
		return nil, err
	}
	p.repaint = make(chan struct{}, 1)
	return &p, nil
}

func (p *Player) Repaint() {
	select {
	case p.repaint <- struct{}{}:
	default:
	}
}

func (p *Player) RepaintZone() {
	go func() {
		p.lock.Lock()
		p.zone.Repaint()
		p.lock.Unlock()
	}()
}

func (p *Player) Paint() (rune, Color) {
	if p.Admin {
		return '♚', "#fa0"
	}
	return p.Hero.Paint()
}

func (p *Player) Think() {
	p.think(false)
}

type ZoneEntryHUD string

func (h ZoneEntryHUD) Paint(setcell func(int, int, rune, Color)) {
	i := 0
	for _, r := range h {
		setcell(i, 0, r, "#fff")
		i++
	}
}

func (h ZoneEntryHUD) Key(code int) bool {
	return false
}

type Hero struct {
	Name_ *Name

	lock     sync.Mutex
	Delay    uint
	Backpack []Object
}

func (h *Hero) Name() string {
	return h.Name_.String()
}

func (h *Hero) Examine() string {
	return "a hero."
}

func (h *Hero) Blocking() bool {
	return false
}

func (h *Hero) Paint() (rune, Color) {
	return '☻', "#fff"
}

func (h *Hero) Think() {
	h.think(true)
}

func (h *Hero) think(ai bool) {
	h.lock.Lock()
	defer h.lock.Unlock()

	if h.Delay > 0 {
		h.Delay--
		return
	}
}

func (h *Hero) InteractOptions() []string {
	return nil
}

func (h *Hero) GiveItem(o Object) {
	h.Backpack = append(h.Backpack, o)
}
<|endoftext|>