<commit_before>// Copyright (c) 2014-2015 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

import (`fmt`; `os`; `time`)

const Ping = 125 // Check time 8 times a second.

type Clock struct {
	halt        bool     // Stop search immediately when set to true.
	optimal     int64    // Minimum time slot for the move.
	softStop    int64    // Target soft time limit to make a move.
	hardStop    int64    // Immediate stop time limit.
	extra       float32  // Extra time factor based on search volatility.
	start       time.Time
	ticker      *time.Ticker
}

type Options struct {
	ponder      bool     // (-) Pondering mode.
	infinite    bool     // (-) Search until the "stop" command.
	maxDepth    int      // Search X plies only.
	maxNodes    int      // (-) Search X nodes only.
	moveTime    int64    // Search exactly X milliseconds per move.
	movesToGo   int64    // Number of moves to make till time control.
	timeLeft    int64    // Time left for all remaining moves.
	timeInc     int64    // Time increment after the move is made.
}

type Engine struct {
	log         bool     // Enable logging.
	uci	    bool     // Use UCI protocol.
	trace       bool     // Trace evaluation scores.
	fancy       bool     // Represent pieces as UTF-8 characters.
	status      uint8    // Engine status.
	logFile     string   // Log file name.
	bookFile    string   // Polyglot opening book file name.
	cacheSize   float64  // Default cache size.
	clock       Clock
	options     Options
}

// Use single statically allocated variable.
var engine Engine

func NewEngine(args ...interface{}) *Engine {
	engine = Engine{}
	for i := 0; i < len(args); i += 2 {
		switch value := args[i+1]; args[i] {
		case `log`:
			engine.log = value.(bool)
		case `logfile`:
			engine.logFile = value.(string)
		case `bookfile`:
			engine.bookFile = value.(string)
		case `uci`:
			engine.uci = value.(bool)
		case `trace`:
			engine.trace = value.(bool)
		case `fancy`:
			engine.fancy = value.(bool)
		case `depth`:
			engine.options.maxDepth = value.(int)
		case `movetime`:
			engine.options.moveTime = int64(value.(int))
		case `cache`:
			switch value.(type) {
			default: // :-)
				engine.cacheSize = value.(float64)
			case int:
				engine.cacheSize = float64(value.(int))
			}
		}
	}

	return &engine
}

// Dumps the string to standard output.
func (e *Engine) print(arg string) *Engine {
	os.Stdout.WriteString(arg)
	return e
}

// Appends the string to log file. No flush is required as f.Write() and friends
// are unbuffered.
func (e *Engine) debug(args ...interface{}) *Engine {
	if len(e.logFile) != 0 {
		logFile, err := os.OpenFile(e.logFile, os.O_CREATE | os.O_WRONLY | os.O_APPEND, 0666)
		if err == nil {
			defer logFile.Close()
			if len := len(args); len > 1 {
				logFile.WriteString(fmt.Sprintf(args[0].(string), args[1:]...))
			} else {
				logFile.WriteString(args[0].(string))
			}
		}
	}
	return e
}

// Dumps the string to standard output and optionally logs it to file.
func (e *Engine) reply(args ...interface{}) *Engine {
	if len := len(args); len > 1 {
		data := fmt.Sprintf(args[0].(string), args[1:]...)
		e.print(data)
		e.debug(data)
	} else if len == 1 {
		e.print(args[0].(string))
		e.debug(args[0].(string))
	}
	return e
}

func (e *Engine) fixedDepth() bool {
	return e.options.maxDepth > 0
}

func (e *Engine) fixedTime() bool {
	return e.options.moveTime > 0
}

func (e *Engine) varyingTime() bool {
	return e.options.moveTime == 0
}

// Returns elapsed time in milliseconds.
func (e *Engine) elapsed(now time.Time) int64 {
	return now.Sub(e.clock.start).Nanoseconds() / 1000000 //int64(time.Millisecond)
}

// Returns remaining search time to make a move. The remaining time extends the
// soft stop estimate based on search volatility factor.
func (e *Engine) remaining() int64 {
	return int64(float32(e.clock.softStop) * e.clock.extra)
}

// Sets extra time factor. For depths 5+ we take into account search volatility,
// i.e. extra time is given for uncertain positions where the best move is not clear.
func (e *Engine) factor(depth int, volatility float32) *Engine {
	if depth < 5 {
		e.clock.extra = 0.75
	} else {
		e.clock.extra = 0.75 * (volatility + 1.0)
	}

	return e
}

// Starts the clock setting ticker callback function. The callback function is
// different for fixed and variable time controls.
func (e *Engine) startClock() *Engine {
	e.clock.halt = false

	if e.options.moveTime == 0 && e.options.timeLeft == 0 {
		return e
	}

	e.clock.start = time.Now()
	e.clock.ticker = time.NewTicker(time.Millisecond * Ping)

	if e.fixedTime() {
		return e.fixedTimeTicker()
	}

	// How long a minute is depends on which side of the bathroom door you're on.
	return e.varyingTimeTicker()
}

// Stop the clock so that the ticker callback function is longer invoked.
func (e *Engine) stopClock() *Engine {
	if e.clock.ticker != nil {
		e.clock.ticker.Stop()
		e.clock.ticker = nil
	}
	return e
}

// Ticker callback for fixed time control (ex. 5s per move). Search gets terminated
// when we've got the move and the elapsed time approaches time-per-move limit.
func (e *Engine) fixedTimeTicker() *Engine {
	go func() {
		if e.clock.ticker == nil {
			return // Nothing to do if the clock has been stopped.
		}
		for now := range e.clock.ticker.C {
			if len(game.rootpv) == 0 {
				continue // Haven't found the move yet.
			}
			if e.elapsed(now) >= e.options.moveTime - Ping {
				e.clock.halt = true
				return
			}
		}
	}()

	return e
}

// Ticker callback for the variable time control (ex. 40 moves in 5 minutes). Search
// termination depends on multiple factors with hard stop being the ultimate limit.
func (e *Engine) varyingTimeTicker() *Engine {
	go func() {
		if e.clock.ticker == nil {
			return // Nothing to do if the clock has been stopped.
		}
		for now := range e.clock.ticker.C {
			if len(game.rootpv) == 0 {
				continue // Haven't found the move yet.
			}
			elapsed := e.elapsed(now)
			if (game.deepening && game.improving && elapsed > e.remaining() * 4 / 5) || elapsed > e.clock.hardStop {
				e.debug("# Halt: Flags %v Elapsed %s Remaining %s Hard stop %s\n",
					game.deepening && game.improving, ms(elapsed), ms(e.remaining() * 4 / 5), ms(e.clock.hardStop))
				e.clock.halt = true
				return
			}
		}
	}()

	return e
}

// Sets fixed search limits such as maximum depth or time to make a move.
func (e *Engine) fixedLimit(options Options) *Engine {
	e.options = options
	return e
}

// Sets variable time control options and calculates soft and hard stop estimates.
func (e *Engine) varyingLimits(options Options) *Engine {

	// Note if it's a new time control before saving the options.
	timeControl := options.movesToGo > e.options.movesToGo
	e.options = options
	e.options.ponder = false
	e.options.infinite = false
	e.options.maxDepth = 0
	e.options.maxNodes = 0
	e.options.moveTime = 0

	// Set default number of moves till the end of the game or time control.
	// TODO: calculate based on game phase.
	if e.options.movesToGo == 0 {
		e.options.movesToGo = 40
	}

	// Calculate hard and soft stop estimates.
	moves := e.options.movesToGo - 1
	hard := options.timeLeft + options.timeInc * moves
	soft := hard / e.options.movesToGo

	// Save initial value of soft stop if it's new time control.
	if timeControl {
		e.clock.optimal = soft
	}
	e.debug("#\n# Make %d moves in %s Optimal %s\n", e.options.movesToGo, ms(e.options.timeLeft), ms(e.clock.optimal))
	e.debug("# First soft stop %s Hard stop %s\n", ms(soft), ms(hard))

	// Adjust hard stop to leave enough time reserve for the remaining moves.
	// The time reserve is calculated as follows:
	//
	//    1 move left: 100% of optimal
	//    2 moves left: 96% of optimal * 2
	//    3 moves left: 92% of optimal * 3
	//    4 moves left: 88% of optimal * 4
	//    ...
	//    10+ moves left: 64% of optimal * number of moves.
	//
	if moves > 0 { // The last move gets all remaining time and doesn't need the reserve.
		percent := max64(64, 100 - 4 * moves)
		hard = hard - e.clock.optimal * moves * percent / 100
		e.debug("# Reserve %d%% Hard stop %s\n", percent, ms(hard))
		if hard < e.clock.optimal {
			hard = min64(e.clock.optimal, options.timeLeft / e.options.movesToGo)
			e.debug("# Optimal adjusted hard stop %s\n", ms(hard))
		}
	}

	// Set the final values for soft and hard stops making sure the soft stop
	// never exceeds the hard one.
	if soft < hard {
		e.clock.softStop, e.clock.hardStop = soft, hard
	} else {
		e.clock.softStop, e.clock.hardStop = hard, soft
	}

	// Keep two ping cycles available to avoid accidental time forefeit.
	e.clock.hardStop -= 2 * Ping
	if e.clock.hardStop < 0 {
		e.clock.hardStop = options.timeLeft // Oh well...
	}

	e.debug("# Final soft stop %s Hard stop %s\n#\n", ms(e.clock.softStop), ms(e.clock.hardStop))

	return e
}
<commit_msg>Avoid time forfeit for time controls with no moves limit<commit_after>// Copyright (c) 2014-2015 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

import (`fmt`; `os`; `time`)

const Ping = 125 // Check time 8 times a second.

type Clock struct {
	halt        bool     // Stop search immediately when set to true.
	optimal     int64    // Minimum time slot for the move.
	softStop    int64    // Target soft time limit to make a move.
	hardStop    int64    // Immediate stop time limit.
	extra       float32  // Extra time factor based on search volatility.
	start       time.Time
	ticker      *time.Ticker
}

type Options struct {
	ponder      bool     // (-) Pondering mode.
	infinite    bool     // (-) Search until the "stop" command.
	maxDepth    int      // Search X plies only.
	maxNodes    int      // (-) Search X nodes only.
	moveTime    int64    // Search exactly X milliseconds per move.
	movesToGo   int64    // Number of moves to make till time control.
	timeLeft    int64    // Time left for all remaining moves.
	timeInc     int64    // Time increment after the move is made.
}

type Engine struct {
	log         bool     // Enable logging.
	uci	    bool     // Use UCI protocol.
	trace       bool     // Trace evaluation scores.
	fancy       bool     // Represent pieces as UTF-8 characters.
	status      uint8    // Engine status.
	logFile     string   // Log file name.
	bookFile    string   // Polyglot opening book file name.
	cacheSize   float64  // Default cache size.
	clock       Clock
	options     Options
}

// Use single statically allocated variable.
var engine Engine

func NewEngine(args ...interface{}) *Engine {
	engine = Engine{}
	for i := 0; i < len(args); i += 2 {
		switch value := args[i+1]; args[i] {
		case `log`:
			engine.log = value.(bool)
		case `logfile`:
			engine.logFile = value.(string)
		case `bookfile`:
			engine.bookFile = value.(string)
		case `uci`:
			engine.uci = value.(bool)
		case `trace`:
			engine.trace = value.(bool)
		case `fancy`:
			engine.fancy = value.(bool)
		case `depth`:
			engine.options.maxDepth = value.(int)
		case `movetime`:
			engine.options.moveTime = int64(value.(int))
		case `cache`:
			switch value.(type) {
			default: // :-)
				engine.cacheSize = value.(float64)
			case int:
				engine.cacheSize = float64(value.(int))
			}
		}
	}

	return &engine
}

// Dumps the string to standard output.
func (e *Engine) print(arg string) *Engine {
	os.Stdout.WriteString(arg)
	return e
}

// Appends the string to log file. No flush is required as f.Write() and friends
// are unbuffered.
func (e *Engine) debug(args ...interface{}) *Engine {
	if len(e.logFile) != 0 {
		logFile, err := os.OpenFile(e.logFile, os.O_CREATE | os.O_WRONLY | os.O_APPEND, 0666)
		if err == nil {
			defer logFile.Close()
			if len := len(args); len > 1 {
				logFile.WriteString(fmt.Sprintf(args[0].(string), args[1:]...))
			} else {
				logFile.WriteString(args[0].(string))
			}
		}
	}
	return e
}

// Dumps the string to standard output and optionally logs it to file.
func (e *Engine) reply(args ...interface{}) *Engine {
	if len := len(args); len > 1 {
		data := fmt.Sprintf(args[0].(string), args[1:]...)
		e.print(data)
		e.debug(data)
	} else if len == 1 {
		e.print(args[0].(string))
		e.debug(args[0].(string))
	}
	return e
}

func (e *Engine) fixedDepth() bool {
	return e.options.maxDepth > 0
}

func (e *Engine) fixedTime() bool {
	return e.options.moveTime > 0
}

func (e *Engine) varyingTime() bool {
	return e.options.moveTime == 0
}

// Returns elapsed time in milliseconds.
func (e *Engine) elapsed(now time.Time) int64 {
	return now.Sub(e.clock.start).Nanoseconds() / 1000000 //int64(time.Millisecond)
}

// Returns remaining search time to make a move. The remaining time extends the
// soft stop estimate based on search volatility factor.
func (e *Engine) remaining() int64 {
	return int64(float32(e.clock.softStop) * e.clock.extra)
}

// Sets extra time factor. For depths 5+ we take into account search volatility,
// i.e. extra time is given for uncertain positions where the best move is not clear.
func (e *Engine) factor(depth int, volatility float32) *Engine {
	if depth < 5 {
		e.clock.extra = 0.75
	} else {
		e.clock.extra = 0.75 * (volatility + 1.0)
	}

	return e
}

// Starts the clock setting ticker callback function. The callback function is
// different for fixed and variable time controls.
func (e *Engine) startClock() *Engine {
	e.clock.halt = false

	if e.options.moveTime == 0 && e.options.timeLeft == 0 {
		return e
	}

	e.clock.start = time.Now()
	e.clock.ticker = time.NewTicker(time.Millisecond * Ping)

	if e.fixedTime() {
		return e.fixedTimeTicker()
	}

	// How long a minute is depends on which side of the bathroom door you're on.
	return e.varyingTimeTicker()
}

// Stop the clock so that the ticker callback function is longer invoked.
func (e *Engine) stopClock() *Engine {
	if e.clock.ticker != nil {
		e.clock.ticker.Stop()
		e.clock.ticker = nil
	}
	return e
}

// Ticker callback for fixed time control (ex. 5s per move). Search gets terminated
// when we've got the move and the elapsed time approaches time-per-move limit.
func (e *Engine) fixedTimeTicker() *Engine {
	go func() {
		if e.clock.ticker == nil {
			return // Nothing to do if the clock has been stopped.
		}
		for now := range e.clock.ticker.C {
			if len(game.rootpv) == 0 {
				continue // Haven't found the move yet.
			}
			if e.elapsed(now) >= e.options.moveTime - Ping {
				e.clock.halt = true
				return
			}
		}
	}()

	return e
}

// Ticker callback for the variable time control (ex. 40 moves in 5 minutes). Search
// termination depends on multiple factors with hard stop being the ultimate limit.
func (e *Engine) varyingTimeTicker() *Engine {
	go func() {
		if e.clock.ticker == nil {
			return // Nothing to do if the clock has been stopped.
		}
		for now := range e.clock.ticker.C {
			if len(game.rootpv) == 0 {
				continue // Haven't found the move yet.
			}
			elapsed := e.elapsed(now)
			if (game.deepening && game.improving && elapsed > e.remaining() * 4 / 5) || elapsed > e.clock.hardStop {
				e.debug("# Halt: Flags %v Elapsed %s Remaining %s Hard stop %s\n",
					game.deepening && game.improving, ms(elapsed), ms(e.remaining() * 4 / 5), ms(e.clock.hardStop))
				e.clock.halt = true
				return
			}
		}
	}()

	return e
}

// Sets fixed search limits such as maximum depth or time to make a move.
func (e *Engine) fixedLimit(options Options) *Engine {
	e.options = options
	return e
}

// Sets variable time control options and calculates soft and hard stop estimates.
func (e *Engine) varyingLimits(options Options) *Engine {

	// Note if it's a new time control before saving the options.
	timeControl := (options.movesToGo > e.options.movesToGo) || (options.movesToGo == 0)
	e.options = options
	e.options.ponder = false
	e.options.infinite = false
	e.options.maxDepth = 0
	e.options.maxNodes = 0
	e.options.moveTime = 0

	// Set default number of moves till the end of the game or time control.
	// TODO: calculate based on game phase.
	if e.options.movesToGo == 0 {
		e.options.movesToGo = 40
	}

	// Calculate hard and soft stop estimates.
	moves := e.options.movesToGo - 1
	hard := options.timeLeft + options.timeInc * moves
	soft := hard / e.options.movesToGo

	// Save initial value of soft stop if it's new time control.
	if timeControl {
		e.clock.optimal = soft
	}
	e.debug("#\n# Make %d moves in %s Optimal %s\n", e.options.movesToGo, ms(e.options.timeLeft), ms(e.clock.optimal))
	e.debug("# First soft stop %s Hard stop %s\n", ms(soft), ms(hard))

	// Adjust hard stop to leave enough time reserve for the remaining moves.
	// The time reserve is calculated as follows:
	//
	//    1 move left: 100% of optimal
	//    2 moves left: 96% of optimal * 2
	//    3 moves left: 92% of optimal * 3
	//    4 moves left: 88% of optimal * 4
	//    ...
	//    10+ moves left: 64% of optimal * number of moves.
	//
	if moves > 0 { // The last move gets all remaining time and doesn't need the reserve.
		percent := max64(64, 100 - 4 * moves)
		hard = hard - e.clock.optimal * moves * percent / 100
		e.debug("# Reserve %d%% Hard stop %s\n", percent, ms(hard))
		if hard < e.clock.optimal {
			hard = min64(e.clock.optimal, options.timeLeft / e.options.movesToGo)
			e.debug("# Optimal adjusted hard stop %s\n", ms(hard))
		}
	}

	// Set the final values for soft and hard stops making sure the soft stop
	// never exceeds the hard one.
	if soft < hard {
		e.clock.softStop, e.clock.hardStop = soft, hard
	} else {
		e.clock.softStop, e.clock.hardStop = hard, soft
	}

	// Keep two ping cycles available to avoid accidental time forefeit.
	e.clock.hardStop -= 2 * Ping
	if e.clock.hardStop < 0 {
		e.clock.hardStop = options.timeLeft // Oh well...
	}

	e.debug("# Final soft stop %s Hard stop %s\n#\n", ms(e.clock.softStop), ms(e.clock.hardStop))

	return e
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	log "github.com/GameGophers/libs/nsq-logger"
	"github.com/boltdb/bolt"
	"golang.org/x/net/context"
	"os"
	"os/signal"
	"strconv"
	"sync"
	"syscall"
	"time"
)

import (
	. "proto"
)

const (
	SERVICE = "[RANK]"
)

const (
	BOLTDB_FILE    = "/data/RANK-DUMP.DAT"
	BOLTDB_BUCKET  = "RANKING"
	CHANGES_SIZE   = 65536
	CHECK_INTERVAL = time.Minute // if ranking has changed, how long to check
)

var (
	OK                    = &Ranking_Nil{}
	ERROR_NAME_NOT_EXISTS = errors.New("name not exists")
)

type server struct {
	ranks   map[uint64]*RankSet
	pending chan uint64
	sync.RWMutex
}

func (s *server) init() {
	s.ranks = make(map[uint64]*RankSet)
	s.pending = make(chan uint64, CHANGES_SIZE)
	s.restore()
	go s.persistence_task()
}

func (s *server) lock_read(f func()) {
	s.RLock()
	defer s.RUnlock()
	f()
}

func (s *server) lock_write(f func()) {
	s.Lock()
	defer s.Unlock()
	f()
}

func (s *server) RankChange(ctx context.Context, p *Ranking_Change) (*Ranking_Nil, error) {
	// check name existence
	var rs *RankSet
	s.lock_write(func() {
		rs = s.ranks[p.SetId]
		if rs == nil {
			rs = NewRankSet()
			s.ranks[p.SetId] = rs
		}
	})

	// apply update on the rankset
	rs.Update(p.UserId, p.Score)
	s.pending <- p.SetId
	return OK, nil
}

func (s *server) QueryRankRange(ctx context.Context, p *Ranking_Range) (*Ranking_RankList, error) {
	var rs *RankSet
	s.lock_read(func() {
		rs = s.ranks[p.SetId]
	})

	if rs == nil {
		return nil, ERROR_NAME_NOT_EXISTS
	}

	ids, cups := rs.GetList(int(p.A), int(p.B))
	return &Ranking_RankList{UserIds: ids, Scores: cups}, nil
}

func (s *server) QueryUsers(ctx context.Context, p *Ranking_Users) (*Ranking_UserList, error) {
	var rs *RankSet
	s.lock_read(func() {
		rs = s.ranks[p.SetId]
	})

	if rs == nil {
		return nil, ERROR_NAME_NOT_EXISTS
	}

	ranks := make([]int32, 0, len(p.UserIds))
	scores := make([]int32, 0, len(p.UserIds))
	for _, id := range p.UserIds {
		rank, score := rs.Rank(id)
		ranks = append(ranks, rank)
		scores = append(scores, score)
	}
	return &Ranking_UserList{Ranks: ranks, Scores: scores}, nil
}

func (s *server) DeleteSet(ctx context.Context, p *Ranking_SetId) (*Ranking_Nil, error) {
	s.lock_write(func() {
		delete(s.ranks, p.SetId)
	})
	return OK, nil
}

func (s *server) DeleteUser(ctx context.Context, p *Ranking_DeleteUserRequest) (*Ranking_Nil, error) {
	var rs *RankSet
	s.lock_read(func() {
		rs = s.ranks[p.SetId]
	})
	if rs == nil {
		return nil, ERROR_NAME_NOT_EXISTS
	}
	rs.Delete(p.UserId)
	return OK, nil
}

// persistence ranking tree into db
func (s *server) persistence_task() {
	timer := time.After(CHECK_INTERVAL)
	db := s.open_db()
	changes := make(map[uint64]bool)
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT)

	for {
		select {
		case key := <-s.pending:
			changes[key] = true
		case <-timer:
			s.dump(db, changes)
			log.Infof("perisisted %v rankset:", len(changes))
			changes = make(map[uint64]bool)
			timer = time.After(CHECK_INTERVAL)
		case nr := <-sig:
			s.dump(db, changes)
			db.Close()
			log.Info(nr)
			os.Exit(0)
		}
	}
}

func (s *server) open_db() *bolt.DB {
	db, err := bolt.Open(BOLTDB_FILE, 0600, nil)
	if err != nil {
		log.Critical(err)
		os.Exit(-1)
	}
	// create bulket
	db.Update(func(tx *bolt.Tx) error {
		_, err := tx.CreateBucketIfNotExists([]byte(BOLTDB_BUCKET))
		if err != nil {
			log.Criticalf("create bucket: %s", err)
			os.Exit(-1)
		}
		return nil
	})
	return db
}

func (s *server) dump(db *bolt.DB, changes map[uint64]bool) {
	for k := range changes {
		// marshal
		var rs *RankSet
		s.lock_read(func() {
			rs = s.ranks[k]
		})
		if rs == nil {
			log.Warning("empty rankset:", k)
			continue
		}

		// serialization and save
		bin, err := rs.Marshal()
		if err != nil {
			log.Critical("cannot marshal:", err)
			os.Exit(-1)
		}

		db.Update(func(tx *bolt.Tx) error {
			b := tx.Bucket([]byte(BOLTDB_BUCKET))
			err := b.Put([]byte(fmt.Sprint(k)), bin)
			return err
		})
	}
}

func (s *server) restore() {
	// restore data from db file
	db := s.open_db()
	defer db.Close()
	db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(BOLTDB_BUCKET))
		c := b.Cursor()

		for k, v := c.First(); k != nil; k, v = c.Next() {
			rs := NewRankSet()
			err := rs.Unmarshal(v)
			if err != nil {
				log.Critical("rank data corrupted:", err)
				os.Exit(-1)
			}
			id, err := strconv.ParseUint(string(k), 0, 64)
			if err != nil {
				log.Critical("chat data corrupted:", err)
				os.Exit(-1)
			}
			s.ranks[id] = rs
		}

		return nil
	})
}
<commit_msg>change cursor to ForEach<commit_after>package main

import (
	"errors"
	"fmt"
	log "github.com/GameGophers/libs/nsq-logger"
	"github.com/boltdb/bolt"
	"golang.org/x/net/context"
	"os"
	"os/signal"
	"strconv"
	"sync"
	"syscall"
	"time"
)

import (
	. "proto"
)

const (
	SERVICE = "[RANK]"
)

const (
	BOLTDB_FILE    = "/data/RANK-DUMP.DAT"
	BOLTDB_BUCKET  = "RANKING"
	CHANGES_SIZE   = 65536
	CHECK_INTERVAL = time.Minute // if ranking has changed, how long to check
)

var (
	OK                    = &Ranking_Nil{}
	ERROR_NAME_NOT_EXISTS = errors.New("name not exists")
)

type server struct {
	ranks   map[uint64]*RankSet
	pending chan uint64
	sync.RWMutex
}

func (s *server) init() {
	s.ranks = make(map[uint64]*RankSet)
	s.pending = make(chan uint64, CHANGES_SIZE)
	s.restore()
	go s.persistence_task()
}

func (s *server) lock_read(f func()) {
	s.RLock()
	defer s.RUnlock()
	f()
}

func (s *server) lock_write(f func()) {
	s.Lock()
	defer s.Unlock()
	f()
}

func (s *server) RankChange(ctx context.Context, p *Ranking_Change) (*Ranking_Nil, error) {
	// check name existence
	var rs *RankSet
	s.lock_write(func() {
		rs = s.ranks[p.SetId]
		if rs == nil {
			rs = NewRankSet()
			s.ranks[p.SetId] = rs
		}
	})

	// apply update on the rankset
	rs.Update(p.UserId, p.Score)
	s.pending <- p.SetId
	return OK, nil
}

func (s *server) QueryRankRange(ctx context.Context, p *Ranking_Range) (*Ranking_RankList, error) {
	var rs *RankSet
	s.lock_read(func() {
		rs = s.ranks[p.SetId]
	})

	if rs == nil {
		return nil, ERROR_NAME_NOT_EXISTS
	}

	ids, cups := rs.GetList(int(p.A), int(p.B))
	return &Ranking_RankList{UserIds: ids, Scores: cups}, nil
}

func (s *server) QueryUsers(ctx context.Context, p *Ranking_Users) (*Ranking_UserList, error) {
	var rs *RankSet
	s.lock_read(func() {
		rs = s.ranks[p.SetId]
	})

	if rs == nil {
		return nil, ERROR_NAME_NOT_EXISTS
	}

	ranks := make([]int32, 0, len(p.UserIds))
	scores := make([]int32, 0, len(p.UserIds))
	for _, id := range p.UserIds {
		rank, score := rs.Rank(id)
		ranks = append(ranks, rank)
		scores = append(scores, score)
	}
	return &Ranking_UserList{Ranks: ranks, Scores: scores}, nil
}

func (s *server) DeleteSet(ctx context.Context, p *Ranking_SetId) (*Ranking_Nil, error) {
	s.lock_write(func() {
		delete(s.ranks, p.SetId)
	})
	return OK, nil
}

func (s *server) DeleteUser(ctx context.Context, p *Ranking_DeleteUserRequest) (*Ranking_Nil, error) {
	var rs *RankSet
	s.lock_read(func() {
		rs = s.ranks[p.SetId]
	})
	if rs == nil {
		return nil, ERROR_NAME_NOT_EXISTS
	}
	rs.Delete(p.UserId)
	return OK, nil
}

// persistence ranking tree into db
func (s *server) persistence_task() {
	timer := time.After(CHECK_INTERVAL)
	db := s.open_db()
	changes := make(map[uint64]bool)
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT)

	for {
		select {
		case key := <-s.pending:
			changes[key] = true
		case <-timer:
			s.dump(db, changes)
			log.Infof("perisisted %v rankset:", len(changes))
			changes = make(map[uint64]bool)
			timer = time.After(CHECK_INTERVAL)
		case nr := <-sig:
			s.dump(db, changes)
			db.Close()
			log.Info(nr)
			os.Exit(0)
		}
	}
}

func (s *server) open_db() *bolt.DB {
	db, err := bolt.Open(BOLTDB_FILE, 0600, nil)
	if err != nil {
		log.Critical(err)
		os.Exit(-1)
	}
	// create bulket
	db.Update(func(tx *bolt.Tx) error {
		_, err := tx.CreateBucketIfNotExists([]byte(BOLTDB_BUCKET))
		if err != nil {
			log.Criticalf("create bucket: %s", err)
			os.Exit(-1)
		}
		return nil
	})
	return db
}

func (s *server) dump(db *bolt.DB, changes map[uint64]bool) {
	for k := range changes {
		// marshal
		var rs *RankSet
		s.lock_read(func() {
			rs = s.ranks[k]
		})
		if rs == nil {
			log.Warning("empty rankset:", k)
			continue
		}

		// serialization and save
		bin, err := rs.Marshal()
		if err != nil {
			log.Critical("cannot marshal:", err)
			os.Exit(-1)
		}

		db.Update(func(tx *bolt.Tx) error {
			b := tx.Bucket([]byte(BOLTDB_BUCKET))
			err := b.Put([]byte(fmt.Sprint(k)), bin)
			return err
		})
	}
}

func (s *server) restore() {
	// restore data from db file
	db := s.open_db()
	defer db.Close()
	db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(BOLTDB_BUCKET))
		b.ForEach(func(k, v []byte) error {
			rs := NewRankSet()
			err := rs.Unmarshal(v)
			if err != nil {
				log.Critical("rank data corrupted:", err)
				os.Exit(-1)
			}
			id, err := strconv.ParseUint(string(k), 0, 64)
			if err != nil {
				log.Critical("chat data corrupted:", err)
				os.Exit(-1)
			}
			s.ranks[id] = rs
			return nil
		})
		return nil
	})
}
<|endoftext|>


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

package main

import (
	"encoding/hex"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"io/ioutil"

	"github.com/pkg/errors"
	"github.com/sdboyer/gps"
)

var ensureCmd = &command{
	fn:   runEnsure,
	name: "ensure",
	short: `[flags] <path>[:alt location][@<version specifier>]
	To ensure a dependency is in your project at a specific version (if specified).
	`,
	long: `
	Run it when
To ensure a new dependency is in your project.
To ensure a dependency is updated.
To the latest version that satisfies constraints.
To a specific version or different constraint.
(With no arguments) To ensure that you have all the dependencies specified by your Manifest + lockfile.

What it does
Download code, placing in the vendor/ directory of the project. Only the packages that are actually used by the current project and its dependencies are included.
Any authentication, proxy settings, or other parameters regarding communicating with external repositories is the responsibility of the underlying VCS tools.
Resolve version constraints
If the set of constraints are not solvable, print an error
Collapse any vendor folders in the downloaded code and its transient deps to the root.
Includes dependencies required by the current project’s tests. There are arguments both for and against including the deps of tests for any transitive deps. Defer on deciding this for now.
Copy the relevant versions of the code to the current project’s vendor directory.
The source of that code is implementation dependant. Probably some kind of local cache of the VCS data (not a GOPATH/workspace).
Write Manifest (if changed) and Lockfile
Print what changed

Flags:
	-update		update all packages
	-n			dry run
	-override <specs>	specify an override constraints for package(s)

Package specs:
	<path>[:alt location][@<version specifier>]

Examples:
Fetch/update github.com/heroku/rollrus to latest version, including transitive dependencies (ensuring it matches the constraints of rollrus, or—if not contrained—their latest versions):
	$ dep ensure github.com/heroku/rollrus
Same dep, but choose any minor patch release in the 0.9.X series, setting the constraint. If another constraint exists that constraint is changed to ~0.9.0:
	$ dep ensure github.com/heroku/rollrus@~0.9.0
Same dep, but choose any release >= 0.9.1 and < 1.0.0, setting/changing constraints:
	$ dep ensure github.com/heroku/rollrus@^0.9.1
Same dep, but updating to 1.0.X:
	$ dep ensure github.com/heroku/rollrus@~1.0.0
Same dep, but fetching from a different location:
	$ dep ensure github.com/heroku/rollrus:git.example.com/foo/bar
Same dep, but check out a specific version or range without updating the Manifest and update the Lockfile. This will fail if the specified version does not satisfy any existing constraints:
	$ dep ensure github.com/heroku/rollrus==1.2.3	# 1.2.3 specifically
	$ dep ensure github.com/heroku/rollrus=^1.2.0	# >= 1.2.0  < 2.0.0
Override any declared dependency range of 'github.com/foo/bar' to have the range of '^0.9.1'. This applies transitively:
	$ dep ensure -override github.com/foo/bar@^0.9.1

Transitive deps are ensured based on constraints in the local Manifest if they exist, then constraints in the dependency’s Manifest file. A lack of constraints defaults to the latest version, eg "^2".

For a description of the version specifier string, see this handy guide from crates.io. We are going to defer on making a final decision about this syntax until we have more experience with it in practice.
	`,
}

func runEnsure(args []string) error {
	p, err := depContext.loadProject("")
	if err != nil {
		return err
	}

	sm, err := depContext.sourceManager()
	if err != nil {
		return err
	}
	defer sm.Release()

	var errs []error
	for _, arg := range args {
		// default persist to manifest
		constraint, err := getProjectConstraint(arg, sm)
		if err != nil {
			errs = append(errs, err)
		}
		p.m.Dependencies[constraint.Ident.ProjectRoot] = gps.ProjectProperties{
			NetworkName: constraint.Ident.NetworkName,
			Constraint:  constraint.Constraint,
		}
		for i, lp := range p.l.P {
			if lp.Ident() == constraint.Ident {
				p.l.P = append(p.l.P[:i], p.l.P[i+1:]...)
				break
			}
		}
	}
	if len(errs) > 0 {
		for err := range errs {
			fmt.Fprintln(os.Stderr, err)
		}
		os.Exit(1)
	}

	fmt.Println(p.absroot)

	params := gps.SolveParameters{
		RootDir:     p.absroot,
		Manifest:    p.m,
		Lock:        p.l,
		Trace:       true,
		TraceLogger: log.New(os.Stdout, "", 0),
	}

	params.RootPackageTree, err = gps.ListPackages(p.absroot, string(p.importroot))
	if err != nil {
		return errors.Wrap(err, "ensure ListPackage for project")
	}
	solver, err := gps.Prepare(params, sm)
	if err != nil {
		return errors.Wrap(err, "ensure Prepare")
	}
	solution, err := solver.Solve()
	if err != nil {
		handleAllTheFailuresOfTheWorld(err)
		return errors.Wrap(err, "ensure Solve()")
	}

	p.l.P = solution.Projects()
	p.l.Memo = solution.InputHash()

	tv, err := ioutil.TempDir("", "vendor")
	if err != nil {
		return errors.Wrap(err, "ensure making temporary vendor")
	}
	defer os.RemoveAll(tv)

	tm, err := ioutil.TempFile("", "manifest")
	if err != nil {
		return errors.Wrap(err, "ensure making temporary manifest")
	}
	tm.Close()
	defer os.Remove(tm.Name())

	tl, err := ioutil.TempFile("", "lock")
	if err != nil {
		return errors.Wrap(err, "ensure making temporary lock file")
	}
	tl.Close()
	defer os.Remove(tl.Name())

	if err := gps.WriteDepTree(tv, p.l, sm, true); err != nil {
		return errors.Wrap(err, "ensure gps.WriteDepTree")
	}

	if err := writeFile(tm.Name(), p.m); err != nil {
		return errors.Wrap(err, "ensure writeFile for manifest")
	}

	if err := writeFile(tl.Name(), p.l); err != nil {
		return errors.Wrap(err, "ensure writeFile for lock")
	}

	if err := os.Rename(tm.Name(), filepath.Join(p.absroot, manifestName)); err != nil {
		return errors.Wrap(err, "ensure moving temp manifest into place!")
	}

	if err := os.Rename(tl.Name(), filepath.Join(p.absroot, lockName)); err != nil {
		return errors.Wrap(err, "ensure moving temp manifest into place!")
	}

	os.RemoveAll(filepath.Join(p.absroot, "vendor"))
	if err := copyFolder(tv, filepath.Join(p.absroot, "vendor")); err != nil {
		return errors.Wrap(err, "ensure moving temp vendor")
	}

	return nil
}

func getProjectConstraint(arg string, sm *gps.SourceMgr) (gps.ProjectConstraint, error) {
	constraint := gps.ProjectConstraint{}

	// try to split on '@'
	atIndex := strings.Index(arg, "@")
	if atIndex > 0 {
		parts := strings.SplitN(arg, "@", 2)
		constraint.Constraint = deduceConstraint(parts[1])
		arg = parts[0]
	}
	// TODO: What if there is no @, assume default branch (which may not be master) ?
	// TODO: if we decide to keep equals.....

	// split on colon if there is a network location
	colonIndex := strings.Index(arg, ":")
	if colonIndex > 0 {
		parts := strings.SplitN(arg, ":", 2)
		arg = parts[0]
		constraint.Ident.NetworkName = parts[1]
	}

	pr, err := sm.DeduceProjectRoot(arg)
	if err != nil {
		return constraint, errors.Wrapf(err, "could not infer project root from dependency path: %s", arg) // this should go through to the user
	}

	if string(pr) != arg {
		return constraint, errors.Wrapf(err, "dependency path %s is not a project root", arg)
	}
	constraint.Ident.ProjectRoot = gps.ProjectRoot(arg)

	return constraint, nil
}

// deduceConstraint tries to puzzle out what kind of version is given in a string -
// semver, a revision, or as a fallback, a plain tag
func deduceConstraint(s string) gps.Constraint {
	// always semver if we can
	c, err := gps.NewSemverConstraint(s)
	if err == nil {
		return c
	}

	slen := len(s)
	if slen == 40 {
		if _, err = hex.DecodeString(s); err == nil {
			// Whether or not it's intended to be a SHA1 digest, this is a
			// valid byte sequence for that, so go with Revision. This
			// covers git and hg
			return gps.Revision(s)
		}
	}
	// Next, try for bzr, which has a three-component GUID separated by
	// dashes. There should be two, but the email part could contain
	// internal dashes
	if strings.Count(s, "-") >= 2 {
		// Work from the back to avoid potential confusion from the email
		i3 := strings.LastIndex(s, "-")
		// Skip if - is last char, otherwise this would panic on bounds err
		if slen == i3+1 {
			return gps.NewVersion(s)
		}

		if _, err = hex.DecodeString(s[i3+1:]); err == nil {
			i2 := strings.LastIndex(s[:i3], "-")
			if _, err = strconv.ParseUint(s[i2+1:i3], 10, 64); err == nil {
				// Getting this far means it'd pretty much be nuts if it's not a
				// bzr rev, so don't bother parsing the email.
				return gps.Revision(s)
			}
		}
	}

	// If not a plain SHA1 or bzr custom GUID, assume a plain version.
	// TODO: if there is amgibuity here, then prompt the user?
	return gps.NewVersion(s)
}

// stolen from k8s https://github.com/jessfraz/kubernetes/blob/2df475da2f7e5c0739afabe356012777b5634951/pkg/volume/volume.go#L249
func copyFolder(source string, dest string) (err error) {
	fi, err := os.Lstat(source)
	if err != nil {
		return err
	}

	err = os.MkdirAll(dest, fi.Mode())
	if err != nil {
		return err
	}

	directory, _ := os.Open(source)

	defer directory.Close()

	objects, err := directory.Readdir(-1)

	for _, obj := range objects {
		if obj.Mode()&os.ModeSymlink != 0 {
			continue
		}

		sourcefilepointer := filepath.Join(source, obj.Name())
		destinationfilepointer := filepath.Join(dest, obj.Name())

		if obj.IsDir() {
			err = copyFolder(sourcefilepointer, destinationfilepointer)
			if err != nil {
				return err
			}
		} else {
			err = copyFile(sourcefilepointer, destinationfilepointer)
			if err != nil {
				return err
			}
		}

	}
	return
}

func copyFile(source string, dest string) (err error) {
	sourcefile, err := os.Open(source)
	if err != nil {
		return err
	}

	defer sourcefile.Close()

	destfile, err := os.Create(dest)
	if err != nil {
		return err
	}

	defer destfile.Close()

	_, err = io.Copy(destfile, sourcefile)
	if err == nil {
		sourceinfo, err := os.Stat(source)
		if err != nil {
			err = os.Chmod(dest, sourceinfo.Mode())
		}

	}
	return
}
<commit_msg>ensure: remove debug output<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"encoding/hex"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"io/ioutil"

	"github.com/pkg/errors"
	"github.com/sdboyer/gps"
)

var ensureCmd = &command{
	fn:   runEnsure,
	name: "ensure",
	short: `[flags] <path>[:alt location][@<version specifier>]
	To ensure a dependency is in your project at a specific version (if specified).
	`,
	long: `
	Run it when
To ensure a new dependency is in your project.
To ensure a dependency is updated.
To the latest version that satisfies constraints.
To a specific version or different constraint.
(With no arguments) To ensure that you have all the dependencies specified by your Manifest + lockfile.

What it does
Download code, placing in the vendor/ directory of the project. Only the packages that are actually used by the current project and its dependencies are included.
Any authentication, proxy settings, or other parameters regarding communicating with external repositories is the responsibility of the underlying VCS tools.
Resolve version constraints
If the set of constraints are not solvable, print an error
Collapse any vendor folders in the downloaded code and its transient deps to the root.
Includes dependencies required by the current project’s tests. There are arguments both for and against including the deps of tests for any transitive deps. Defer on deciding this for now.
Copy the relevant versions of the code to the current project’s vendor directory.
The source of that code is implementation dependant. Probably some kind of local cache of the VCS data (not a GOPATH/workspace).
Write Manifest (if changed) and Lockfile
Print what changed

Flags:
	-update		update all packages
	-n			dry run
	-override <specs>	specify an override constraints for package(s)

Package specs:
	<path>[:alt location][@<version specifier>]

Examples:
Fetch/update github.com/heroku/rollrus to latest version, including transitive dependencies (ensuring it matches the constraints of rollrus, or—if not contrained—their latest versions):
	$ dep ensure github.com/heroku/rollrus
Same dep, but choose any minor patch release in the 0.9.X series, setting the constraint. If another constraint exists that constraint is changed to ~0.9.0:
	$ dep ensure github.com/heroku/rollrus@~0.9.0
Same dep, but choose any release >= 0.9.1 and < 1.0.0, setting/changing constraints:
	$ dep ensure github.com/heroku/rollrus@^0.9.1
Same dep, but updating to 1.0.X:
	$ dep ensure github.com/heroku/rollrus@~1.0.0
Same dep, but fetching from a different location:
	$ dep ensure github.com/heroku/rollrus:git.example.com/foo/bar
Same dep, but check out a specific version or range without updating the Manifest and update the Lockfile. This will fail if the specified version does not satisfy any existing constraints:
	$ dep ensure github.com/heroku/rollrus==1.2.3	# 1.2.3 specifically
	$ dep ensure github.com/heroku/rollrus=^1.2.0	# >= 1.2.0  < 2.0.0
Override any declared dependency range of 'github.com/foo/bar' to have the range of '^0.9.1'. This applies transitively:
	$ dep ensure -override github.com/foo/bar@^0.9.1

Transitive deps are ensured based on constraints in the local Manifest if they exist, then constraints in the dependency’s Manifest file. A lack of constraints defaults to the latest version, eg "^2".

For a description of the version specifier string, see this handy guide from crates.io. We are going to defer on making a final decision about this syntax until we have more experience with it in practice.
	`,
}

func runEnsure(args []string) error {
	p, err := depContext.loadProject("")
	if err != nil {
		return err
	}

	sm, err := depContext.sourceManager()
	if err != nil {
		return err
	}
	defer sm.Release()

	var errs []error
	for _, arg := range args {
		// default persist to manifest
		constraint, err := getProjectConstraint(arg, sm)
		if err != nil {
			errs = append(errs, err)
		}
		p.m.Dependencies[constraint.Ident.ProjectRoot] = gps.ProjectProperties{
			NetworkName: constraint.Ident.NetworkName,
			Constraint:  constraint.Constraint,
		}
		for i, lp := range p.l.P {
			if lp.Ident() == constraint.Ident {
				p.l.P = append(p.l.P[:i], p.l.P[i+1:]...)
				break
			}
		}
	}
	if len(errs) > 0 {
		for err := range errs {
			fmt.Fprintln(os.Stderr, err)
		}
		os.Exit(1)
	}

	params := gps.SolveParameters{
		RootDir:     p.absroot,
		Manifest:    p.m,
		Lock:        p.l,
		Trace:       true,
		TraceLogger: log.New(os.Stdout, "", 0),
	}

	params.RootPackageTree, err = gps.ListPackages(p.absroot, string(p.importroot))
	if err != nil {
		return errors.Wrap(err, "ensure ListPackage for project")
	}
	solver, err := gps.Prepare(params, sm)
	if err != nil {
		return errors.Wrap(err, "ensure Prepare")
	}
	solution, err := solver.Solve()
	if err != nil {
		handleAllTheFailuresOfTheWorld(err)
		return errors.Wrap(err, "ensure Solve()")
	}

	p.l.P = solution.Projects()
	p.l.Memo = solution.InputHash()

	tv, err := ioutil.TempDir("", "vendor")
	if err != nil {
		return errors.Wrap(err, "ensure making temporary vendor")
	}
	defer os.RemoveAll(tv)

	tm, err := ioutil.TempFile("", "manifest")
	if err != nil {
		return errors.Wrap(err, "ensure making temporary manifest")
	}
	tm.Close()
	defer os.Remove(tm.Name())

	tl, err := ioutil.TempFile("", "lock")
	if err != nil {
		return errors.Wrap(err, "ensure making temporary lock file")
	}
	tl.Close()
	defer os.Remove(tl.Name())

	if err := gps.WriteDepTree(tv, p.l, sm, true); err != nil {
		return errors.Wrap(err, "ensure gps.WriteDepTree")
	}

	if err := writeFile(tm.Name(), p.m); err != nil {
		return errors.Wrap(err, "ensure writeFile for manifest")
	}

	if err := writeFile(tl.Name(), p.l); err != nil {
		return errors.Wrap(err, "ensure writeFile for lock")
	}

	if err := os.Rename(tm.Name(), filepath.Join(p.absroot, manifestName)); err != nil {
		return errors.Wrap(err, "ensure moving temp manifest into place!")
	}

	if err := os.Rename(tl.Name(), filepath.Join(p.absroot, lockName)); err != nil {
		return errors.Wrap(err, "ensure moving temp manifest into place!")
	}

	os.RemoveAll(filepath.Join(p.absroot, "vendor"))
	if err := copyFolder(tv, filepath.Join(p.absroot, "vendor")); err != nil {
		return errors.Wrap(err, "ensure moving temp vendor")
	}

	return nil
}

func getProjectConstraint(arg string, sm *gps.SourceMgr) (gps.ProjectConstraint, error) {
	constraint := gps.ProjectConstraint{}

	// try to split on '@'
	atIndex := strings.Index(arg, "@")
	if atIndex > 0 {
		parts := strings.SplitN(arg, "@", 2)
		constraint.Constraint = deduceConstraint(parts[1])
		arg = parts[0]
	}
	// TODO: What if there is no @, assume default branch (which may not be master) ?
	// TODO: if we decide to keep equals.....

	// split on colon if there is a network location
	colonIndex := strings.Index(arg, ":")
	if colonIndex > 0 {
		parts := strings.SplitN(arg, ":", 2)
		arg = parts[0]
		constraint.Ident.NetworkName = parts[1]
	}

	pr, err := sm.DeduceProjectRoot(arg)
	if err != nil {
		return constraint, errors.Wrapf(err, "could not infer project root from dependency path: %s", arg) // this should go through to the user
	}

	if string(pr) != arg {
		return constraint, errors.Wrapf(err, "dependency path %s is not a project root", arg)
	}
	constraint.Ident.ProjectRoot = gps.ProjectRoot(arg)

	return constraint, nil
}

// deduceConstraint tries to puzzle out what kind of version is given in a string -
// semver, a revision, or as a fallback, a plain tag
func deduceConstraint(s string) gps.Constraint {
	// always semver if we can
	c, err := gps.NewSemverConstraint(s)
	if err == nil {
		return c
	}

	slen := len(s)
	if slen == 40 {
		if _, err = hex.DecodeString(s); err == nil {
			// Whether or not it's intended to be a SHA1 digest, this is a
			// valid byte sequence for that, so go with Revision. This
			// covers git and hg
			return gps.Revision(s)
		}
	}
	// Next, try for bzr, which has a three-component GUID separated by
	// dashes. There should be two, but the email part could contain
	// internal dashes
	if strings.Count(s, "-") >= 2 {
		// Work from the back to avoid potential confusion from the email
		i3 := strings.LastIndex(s, "-")
		// Skip if - is last char, otherwise this would panic on bounds err
		if slen == i3+1 {
			return gps.NewVersion(s)
		}

		if _, err = hex.DecodeString(s[i3+1:]); err == nil {
			i2 := strings.LastIndex(s[:i3], "-")
			if _, err = strconv.ParseUint(s[i2+1:i3], 10, 64); err == nil {
				// Getting this far means it'd pretty much be nuts if it's not a
				// bzr rev, so don't bother parsing the email.
				return gps.Revision(s)
			}
		}
	}

	// If not a plain SHA1 or bzr custom GUID, assume a plain version.
	// TODO: if there is amgibuity here, then prompt the user?
	return gps.NewVersion(s)
}

// stolen from k8s https://github.com/jessfraz/kubernetes/blob/2df475da2f7e5c0739afabe356012777b5634951/pkg/volume/volume.go#L249
func copyFolder(source string, dest string) (err error) {
	fi, err := os.Lstat(source)
	if err != nil {
		return err
	}

	err = os.MkdirAll(dest, fi.Mode())
	if err != nil {
		return err
	}

	directory, _ := os.Open(source)

	defer directory.Close()

	objects, err := directory.Readdir(-1)

	for _, obj := range objects {
		if obj.Mode()&os.ModeSymlink != 0 {
			continue
		}

		sourcefilepointer := filepath.Join(source, obj.Name())
		destinationfilepointer := filepath.Join(dest, obj.Name())

		if obj.IsDir() {
			err = copyFolder(sourcefilepointer, destinationfilepointer)
			if err != nil {
				return err
			}
		} else {
			err = copyFile(sourcefilepointer, destinationfilepointer)
			if err != nil {
				return err
			}
		}

	}
	return
}

func copyFile(source string, dest string) (err error) {
	sourcefile, err := os.Open(source)
	if err != nil {
		return err
	}

	defer sourcefile.Close()

	destfile, err := os.Create(dest)
	if err != nil {
		return err
	}

	defer destfile.Close()

	_, err = io.Copy(destfile, sourcefile)
	if err == nil {
		sourceinfo, err := os.Stat(source)
		if err != nil {
			err = os.Chmod(dest, sourceinfo.Mode())
		}

	}
	return
}
<|endoftext|>


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

package twodee

import (
	"time"
)

type Entity interface {
	Pos() Point
	Bounds() Rectangle
	Frame() int
	Rotation() float32
	MoveTo(Point)
	Update(elapsed time.Duration)
}

type BaseEntity struct {
	pos      Point
	halfW    float32
	halfH    float32
	rotation float32
	frame    int
}

func NewBaseEntity(x, y, w, h, r float32, frame int) *BaseEntity {
	return &BaseEntity{
		pos:      Pt(x, y),
		halfW:    w / 2.0,
		halfH:    h / 2.0,
		rotation: r,
		frame:    frame,
	}
}

func (e *BaseEntity) Bounds() Rectangle {
	return Rect(e.pos.X-e.halfW, e.pos.Y-e.halfH, e.pos.X+e.halfW, e.pos.Y+e.halfH)
}

func (e *BaseEntity) Pos() Point {
	return e.pos
}

func (e *BaseEntity) MoveTo(pt Point) {
	e.pos = pt
}

func (e *BaseEntity) Frame() int {
	return e.frame
}

func (e *BaseEntity) Rotation() float32 {
	return e.rotation
}

func (e *BaseEntity) Update(elapsed time.Duration) {
}

type AnimatingEntity struct {
	animation *Animation
	*BaseEntity
}

func NewAnimatingEntity(x, y, w, h, r float32, l time.Duration, f []int) *AnimatingEntity {
	return &AnimatingEntity{
		animation:  NewAnimation(l, f),
		BaseEntity: NewBaseEntity(x, y, w, h, r, 0),
	}
}

func (e *AnimatingEntity) Update(elapsed time.Duration) {
	e.animation.Update(elapsed)
}

func (e *AnimatingEntity) Frame() int {
	return e.animation.Current
}
<commit_msg>Be able to update animating entity frames.<commit_after>// Copyright 2014 Arne Roomann-Kurrik
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package twodee

import (
	"time"
)

type Entity interface {
	Pos() Point
	Bounds() Rectangle
	Frame() int
	Rotation() float32
	MoveTo(Point)
	Update(elapsed time.Duration)
}

type BaseEntity struct {
	pos      Point
	halfW    float32
	halfH    float32
	rotation float32
	frame    int
}

func NewBaseEntity(x, y, w, h, r float32, frame int) *BaseEntity {
	return &BaseEntity{
		pos:      Pt(x, y),
		halfW:    w / 2.0,
		halfH:    h / 2.0,
		rotation: r,
		frame:    frame,
	}
}

func (e *BaseEntity) Bounds() Rectangle {
	return Rect(e.pos.X-e.halfW, e.pos.Y-e.halfH, e.pos.X+e.halfW, e.pos.Y+e.halfH)
}

func (e *BaseEntity) Pos() Point {
	return e.pos
}

func (e *BaseEntity) MoveTo(pt Point) {
	e.pos = pt
}

func (e *BaseEntity) Frame() int {
	return e.frame
}

func (e *BaseEntity) Rotation() float32 {
	return e.rotation
}

func (e *BaseEntity) Update(elapsed time.Duration) {
}

type AnimatingEntity struct {
	animation *Animation
	*BaseEntity
}

func NewAnimatingEntity(x, y, w, h, r float32, l time.Duration, f []int) *AnimatingEntity {
	return &AnimatingEntity{
		animation:  NewAnimation(l, f),
		BaseEntity: NewBaseEntity(x, y, w, h, r, 0),
	}
}

func (e *AnimatingEntity) SetFrames(f []int) {
	e.animation.Sequence = f
}

func (e *AnimatingEntity) Update(elapsed time.Duration) {
	e.animation.Update(elapsed)
}

func (e *AnimatingEntity) Frame() int {
	return e.animation.Current
}
<|endoftext|>


<commit_before>package chewcrew

import (
	"errors"
	"fmt"
	"runtime"
)

const maxStackLength = 100000

// A simple wrapper around native errors that also provides a stack trace and an optional context
// message.
type Error struct {
	E       error
	Context string
	Stack   string
}

func (e Error) Error() string {
	s := ""

	// prefix with context if there is one
	if e.Context != "" {
		s = e.Context + " : "
	}

	// provide error message and stack
	s += fmt.Sprintf("%s\n%s", e.E.Error(), e.Stack)

	return s
}

// Create an error with a basic message.
func NewError(s string) error {
	err := errors.New(s)

	stackBuf := make([]byte, maxStackLength, maxStackLength)
	bytesRead := runtime.Stack(stackBuf, false)
	stack := string(stackBuf[:bytesRead])

	return Error{err, "", stack}
}

// Create an error with a formatted message.
func NewErrorf(f string, a ...interface{}) error {
	err := fmt.Errorf(f, a...)

	stackBuf := make([]byte, maxStackLength, maxStackLength)
	bytesRead := runtime.Stack(stackBuf, false)
	stack := string(stackBuf[:bytesRead])

	return Error{err, "", stack}
}

// Useful for wrapping errors received from core and third party libraries, providing them a stack
// trace.
func NewMaskedError(underlying error) error {
	stackBuf := make([]byte, maxStackLength, maxStackLength)
	bytesRead := runtime.Stack(stackBuf, false)
	stack := string(stackBuf[:bytesRead])

	return Error{underlying, "", stack}
}

// Useful for wrapping errors received from core and third party libraries, providing them a stack
// trace. Additionally, this method allows for a message to be saved for context.
func NewMaskedErrorWithContext(underlying error, context string) error {
	stackBuf := make([]byte, maxStackLength, maxStackLength)
	bytesRead := runtime.Stack(stackBuf, false)
	stack := string(stackBuf[:bytesRead])

	return Error{underlying, context, stack}
}

// Useful for wrapping errors received from core and third party libraries, providing them a stack
// trace. Additionally, this method allows for a formatted message to be saved for context.
func NewMaskedErrorWithContextf(underlying error, f string, a ...interface{}) error {
	stackBuf := make([]byte, maxStackLength, maxStackLength)
	bytesRead := runtime.Stack(stackBuf, false)
	stack := string(stackBuf[:bytesRead])

	return Error{underlying, fmt.Sprintf(f, a...), stack}
}
<commit_msg>clean up errors file<commit_after>package chewcrew

import (
	"errors"
	"fmt"
	"runtime"
)

const maxStackLength = 100000

// A simple wrapper around native errors that also provides a stack trace and an optional context
// message. Implements error interface.
type Error struct {
	E       error
	Context string
	Stack   string
}

func (e Error) Error() string {
	s := ""

	// prefix with context if there is one
	if e.Context != "" {
		s = e.Context + " : "
	}

	// provide error message and stack
	s += fmt.Sprintf("%s\n%s", e.E.Error(), e.Stack)

	return s
}

// Create an error with a basic message.
func NewError(s string) Error {
	err := errors.New(s)

	stackBuf := make([]byte, maxStackLength, maxStackLength)
	bytesRead := runtime.Stack(stackBuf, false)
	stack := string(stackBuf[:bytesRead])

	return Error{err, "", stack}
}

// Create an error with a formatted message.
func NewErrorf(f string, a ...interface{}) Error {
	err := fmt.Errorf(f, a...)

	stackBuf := make([]byte, maxStackLength, maxStackLength)
	bytesRead := runtime.Stack(stackBuf, false)
	stack := string(stackBuf[:bytesRead])

	return Error{err, "", stack}
}

// Useful for wrapping errors received from core and third party libraries, providing them a stack
// trace.
func NewMaskedError(underlying error) Error {
	stackBuf := make([]byte, maxStackLength, maxStackLength)
	bytesRead := runtime.Stack(stackBuf, false)
	stack := string(stackBuf[:bytesRead])

	return Error{underlying, "", stack}
}

// Useful for wrapping errors received from core and third party libraries, providing them a stack
// trace. Additionally, this method allows for a message to be saved for context.
func NewMaskedErrorWithContext(underlying error, context string) Error {
	stackBuf := make([]byte, maxStackLength, maxStackLength)
	bytesRead := runtime.Stack(stackBuf, false)
	stack := string(stackBuf[:bytesRead])

	return Error{underlying, context, stack}
}

// Useful for wrapping errors received from core and third party libraries, providing them a stack
// trace. Additionally, this method allows for a formatted message to be saved for context.
func NewMaskedErrorWithContextf(underlying error, f string, a ...interface{}) Error {
	stackBuf := make([]byte, maxStackLength, maxStackLength)
	bytesRead := runtime.Stack(stackBuf, false)
	stack := string(stackBuf[:bytesRead])

	return Error{underlying, fmt.Sprintf(f, a...), stack}
}
<|endoftext|>


<commit_before>package TM_EC

import (
	"strings"
)

type Errors struct {
	errors []error
}

func (errs Errors) Error() string {
	var errors []string
	for _, err := range errs.errors {
		errors = append(errors, err.Error())
	}

	return strings.Join(errors, ";")
}

func (errs *Errors) AddError(errors ...error) {
	for _, err := range errs.errors {
		if err != nil {
			if e, ok := err.(errorsInterface); ok {
				errs.errors = append(errs.errors, e.GetErrors()...)
			} else {
				errs.errors = append(errs.errors, err)
			}
		}
	}
}

func (errs Errors) HasError() bool {
	return len(errs.errors) != 0
}

//	'GetErrors'
func (errs Errors) GetErrors() []error {
	return errs.getErrors()
}

type errorsInterface interface {
	GetErrors() []error
}
<commit_msg>fix errors.go again<commit_after>package TM_EC

import (
	"strings"
)

type Errors struct {
	errors []error
}

func (errs Errors) Error() string {
	var errors []string
	for _, err := range errs.errors {
		errors = append(errors, err.Error())
	}

	return strings.Join(errors, ";")
}

func (errs *Errors) AddError(errors ...error) {
	for _, err := range errs.errors {
		if err != nil {
			if e, ok := err.(errorsInterface); ok {
				errs.errors = append(errs.errors, e.GetErrors()...)
			} else {
				errs.errors = append(errs.errors, err)
			}
		}
	}
}

func (errs Errors) HasError() bool {
	return len(errs.errors) != 0
}

//	'GetErrors'
func (errs Errors) GetErrors() []error {
	return errs.errors
}

type errorsInterface interface {
	GetErrors() []error
}
<|endoftext|>


<commit_before>package stun

import "errors"

// DecodeErr records an error and place when it is occurred.
type DecodeErr struct {
	Place   DecodeErrPlace
	Message string
}

// IsInvalidCookie returns true if error means that magic cookie
// value is invalid.
func (e DecodeErr) IsInvalidCookie() bool {
	return e.Place == DecodeErrPlace{"message", "cookie"}
}

// IsPlaceParent reports if error place parent is p.
func (e DecodeErr) IsPlaceParent(p string) bool {
	return e.Place.Parent == p
}

// IsPlaceChildren reports if error place children is c.
func (e DecodeErr) IsPlaceChildren(c string) bool {
	return e.Place.Children == c
}

// IsPlace reports if error place is p.
func (e DecodeErr) IsPlace(p DecodeErrPlace) bool {
	return e.Place == p
}

// DecodeErrPlace records a place where error is occurred.
type DecodeErrPlace struct {
	Parent   string
	Children string
}

func (p DecodeErrPlace) String() string {
	return p.Parent + "/" + p.Children
}

func (e DecodeErr) Error() string {
	return "BadFormat for " + e.Place.String() + ": " + e.Message
}

func newDecodeErr(parent, children, message string) *DecodeErr {
	return &DecodeErr{
		Place:   DecodeErrPlace{Parent: parent, Children: children},
		Message: message,
	}
}

// TODO(ar): rewrite errors to be more precise.
func newAttrDecodeErr(children, message string) *DecodeErr {
	return newDecodeErr("attribute", children, message)
}

// ErrAttrSizeInvalid means that decoded attribute size is invalid.
//
// DEPRECATED: use ErrAttributeSizeInvalid.
var ErrAttrSizeInvalid = errors.New("attribute size is invalid")

// ErrAttributeSizeInvalid means that decoded attribute size is invalid.
var ErrAttributeSizeInvalid = ErrAttrSizeInvalid

// ErrAttributeSizeOverflow means that decoded attribute size is too big.
var ErrAttributeSizeOverflow = errors.New("attribute size overflow")
<commit_msg>errors: remove ErrAttributeSizeInvalid<commit_after>package stun

import "errors"

// DecodeErr records an error and place when it is occurred.
type DecodeErr struct {
	Place   DecodeErrPlace
	Message string
}

// IsInvalidCookie returns true if error means that magic cookie
// value is invalid.
func (e DecodeErr) IsInvalidCookie() bool {
	return e.Place == DecodeErrPlace{"message", "cookie"}
}

// IsPlaceParent reports if error place parent is p.
func (e DecodeErr) IsPlaceParent(p string) bool {
	return e.Place.Parent == p
}

// IsPlaceChildren reports if error place children is c.
func (e DecodeErr) IsPlaceChildren(c string) bool {
	return e.Place.Children == c
}

// IsPlace reports if error place is p.
func (e DecodeErr) IsPlace(p DecodeErrPlace) bool {
	return e.Place == p
}

// DecodeErrPlace records a place where error is occurred.
type DecodeErrPlace struct {
	Parent   string
	Children string
}

func (p DecodeErrPlace) String() string {
	return p.Parent + "/" + p.Children
}

func (e DecodeErr) Error() string {
	return "BadFormat for " + e.Place.String() + ": " + e.Message
}

func newDecodeErr(parent, children, message string) *DecodeErr {
	return &DecodeErr{
		Place:   DecodeErrPlace{Parent: parent, Children: children},
		Message: message,
	}
}

// TODO(ar): rewrite errors to be more precise.
func newAttrDecodeErr(children, message string) *DecodeErr {
	return newDecodeErr("attribute", children, message)
}

// ErrAttributeSizeInvalid means that decoded attribute size is invalid.
var ErrAttributeSizeInvalid = errors.New("attribute size is invalid")

// ErrAttributeSizeOverflow means that decoded attribute size is too big.
var ErrAttributeSizeOverflow = errors.New("attribute size overflow")
<|endoftext|>


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

package date

import (
	"fmt"
	"regexp"
	"strconv"
	"time"
)

// These are predefined layouts for use in Date.Format and Date.Parse.
// The reference date used in the layouts is the same date used by the
// time package in the standard library:
//     Monday, Jan 2, 2006
// To define your own format, write down what the reference date would look
// like formatted your way; see the values of the predefined layouts for
// examples. The model is to demonstrate what the reference date looks like
// so that the Parse function and Format method can apply the same
// transformation to a general date value.
const (
	ISO8601  = "2006-01-02" // ISO 8601 extended format
	ISO8601B = "20060102"   // ISO 8601 basic format
	RFC822   = "02-Jan-06"
	RFC822W  = "Mon, 02-Jan-06" // RFC822 with day of the week
	RFC850   = "Monday, 02-Jan-06"
	RFC1123  = "02 Jan 2006"
	RFC1123W = "Mon, 02 Jan 2006" // RFC1123 with day of the week
	RFC3339  = "2006-01-02"
)

// reISO8601 is the regular expression used to parse date strings in the
// ISO 8601 extended format, with or without an expanded year representation.
var reISO8601 = regexp.MustCompile(`^([-+]?\d{4,})-(\d{2})-(\d{2})$`)

// ParseISO parses an ISO 8601 formatted string and returns the date value it represents.
// In addition to the common extended format (e.g. 2006-01-02), this function
// accepts date strings using the expanded year representation
// with possibly extra year digits beyond the prescribed four-digit minimum
// and with a + or - sign prefix (e.g. , "+12345-06-07", "-0987-06-05").
//
// Note that ParseISO is a little looser than the ISO 8601 standard and will
// be happy to parse dates with a year longer than the four-digit minimum even
// if they are missing the + sign prefix.
//
// Function Date.Parse can be used to parse date strings in other formats, but it
// is currently not able to parse ISO 8601 formatted strings that use the
// expanded year format.
func ParseISO(value string) (Date, error) {
	m := reISO8601.FindStringSubmatch(value)
	if len(m) != 4 {
		return Date{}, fmt.Errorf("Date.ParseISO: cannot parse %s", value)
	}
	// No need to check for errors since the regexp guarantees the matches
	// are valid integers
	year, _ := strconv.Atoi(m[1])
	month, _ := strconv.Atoi(m[2])
	day, _ := strconv.Atoi(m[3])

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

	return Date{encode(t)}, nil
}

// Parse parses a formatted string and returns the Date value it represents.
// The layout defines the format by showing how the reference date, defined
// to be
//     Monday, Jan 2, 2006
// would be interpreted if it were the value; it serves as an example of the
// input format. The same interpretation will then be made to the input string.
//
// This function actually uses time.Parse to parse the input and can use any
// layout accepted by time.Parse, but returns only the date part of the
// parsed Time value.
//
// This function cannot currently parse ISO 8601 strings that use the expanded
// year format; you should use Date.ParseISO to parse those strings correctly.
func Parse(layout, value string) (Date, error) {
	t, err := time.Parse(layout, value)
	if err != nil {
		return Date{0}, err
	}
	return Date{encode(t)}, nil
}

// String returns the time formatted in ISO 8601 extended format
// (e.g. "2006-01-02").  If the year of the date falls outside the
// [0,9999] range, this format produces an expanded year representation
// with possibly extra year digits beyond the prescribed four-digit minimum
// and with a + or - sign prefix (e.g. , "+12345-06-07", "-0987-06-05").
func (d Date) String() string {
	year, month, day := d.Date()
	if 0 <= year && year < 10000 {
		return fmt.Sprintf("%04d-%02d-%02d", year, month, day)
	}
	return fmt.Sprintf("%+05d-%02d-%02d", year, month, day)
}

// FormatISO returns a textual representation of the date value formatted
// according to the expanded year variant of the ISO 8601 extended format;
// the year of the date is represented as a signed integer using the
// specified number of digits (ignored if less than four).
// The string representation of the year will take more than the specified
// number of digits if the magniture of the year is too large to fit.
//
// Function Date.Format can be used to format Date values in other formats,
// but it is currently not able to format dates according to the expanded
// year variant of the ISO 8601 format.
func (d Date) FormatISO(yearDigits int) string {
	n := 5 // four-digit minimum plus sign
	if yearDigits > 4 {
		n += yearDigits - 4
	}
	year, month, day := d.Date()
	return fmt.Sprintf("%+0*d-%02d-%02d", n, year, month, day)
}

// Format returns a textual representation of the date value formatted according
// to layout, which defines the format by showing how the reference date,
// defined to be
//     Mon, Jan 2, 2006
// would be displayed if it were the value; it serves as an example of the
// desired output.
//
// This function actually uses time.Format to format the input and can use any
// layout accepted by time.Format by extending its date to a time at
// 00:00:00.000 UTC.
//
// This function cannot currently format Date values according to the expanded
// year variant of ISO 8601; you should use Date.FormatISO to that effect.
func (d Date) Format(layout string) string {
	t := decode(d.day)
	return t.Format(layout)
}
<commit_msg>Fixed typo in documentation<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package date

import (
	"fmt"
	"regexp"
	"strconv"
	"time"
)

// These are predefined layouts for use in Date.Format and Date.Parse.
// The reference date used in the layouts is the same date used by the
// time package in the standard library:
//     Monday, Jan 2, 2006
// To define your own format, write down what the reference date would look
// like formatted your way; see the values of the predefined layouts for
// examples. The model is to demonstrate what the reference date looks like
// so that the Parse function and Format method can apply the same
// transformation to a general date value.
const (
	ISO8601  = "2006-01-02" // ISO 8601 extended format
	ISO8601B = "20060102"   // ISO 8601 basic format
	RFC822   = "02-Jan-06"
	RFC822W  = "Mon, 02-Jan-06" // RFC822 with day of the week
	RFC850   = "Monday, 02-Jan-06"
	RFC1123  = "02 Jan 2006"
	RFC1123W = "Mon, 02 Jan 2006" // RFC1123 with day of the week
	RFC3339  = "2006-01-02"
)

// reISO8601 is the regular expression used to parse date strings in the
// ISO 8601 extended format, with or without an expanded year representation.
var reISO8601 = regexp.MustCompile(`^([-+]?\d{4,})-(\d{2})-(\d{2})$`)

// ParseISO parses an ISO 8601 formatted string and returns the date value it represents.
// In addition to the common extended format (e.g. 2006-01-02), this function
// accepts date strings using the expanded year representation
// with possibly extra year digits beyond the prescribed four-digit minimum
// and with a + or - sign prefix (e.g. , "+12345-06-07", "-0987-06-05").
//
// Note that ParseISO is a little looser than the ISO 8601 standard and will
// be happy to parse dates with a year longer than the four-digit minimum even
// if they are missing the + sign prefix.
//
// Function Date.Parse can be used to parse date strings in other formats, but it
// is currently not able to parse ISO 8601 formatted strings that use the
// expanded year format.
func ParseISO(value string) (Date, error) {
	m := reISO8601.FindStringSubmatch(value)
	if len(m) != 4 {
		return Date{}, fmt.Errorf("Date.ParseISO: cannot parse %s", value)
	}
	// No need to check for errors since the regexp guarantees the matches
	// are valid integers
	year, _ := strconv.Atoi(m[1])
	month, _ := strconv.Atoi(m[2])
	day, _ := strconv.Atoi(m[3])

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

	return Date{encode(t)}, nil
}

// Parse parses a formatted string and returns the Date value it represents.
// The layout defines the format by showing how the reference date, defined
// to be
//     Monday, Jan 2, 2006
// would be interpreted if it were the value; it serves as an example of the
// input format. The same interpretation will then be made to the input string.
//
// This function actually uses time.Parse to parse the input and can use any
// layout accepted by time.Parse, but returns only the date part of the
// parsed Time value.
//
// This function cannot currently parse ISO 8601 strings that use the expanded
// year format; you should use Date.ParseISO to parse those strings correctly.
func Parse(layout, value string) (Date, error) {
	t, err := time.Parse(layout, value)
	if err != nil {
		return Date{0}, err
	}
	return Date{encode(t)}, nil
}

// String returns the time formatted in ISO 8601 extended format
// (e.g. "2006-01-02").  If the year of the date falls outside the
// [0,9999] range, this format produces an expanded year representation
// with possibly extra year digits beyond the prescribed four-digit minimum
// and with a + or - sign prefix (e.g. , "+12345-06-07", "-0987-06-05").
func (d Date) String() string {
	year, month, day := d.Date()
	if 0 <= year && year < 10000 {
		return fmt.Sprintf("%04d-%02d-%02d", year, month, day)
	}
	return fmt.Sprintf("%+05d-%02d-%02d", year, month, day)
}

// FormatISO returns a textual representation of the date value formatted
// according to the expanded year variant of the ISO 8601 extended format;
// the year of the date is represented as a signed integer using the
// specified number of digits (ignored if less than four).
// The string representation of the year will take more than the specified
// number of digits if the magnitude of the year is too large to fit.
//
// Function Date.Format can be used to format Date values in other formats,
// but it is currently not able to format dates according to the expanded
// year variant of the ISO 8601 format.
func (d Date) FormatISO(yearDigits int) string {
	n := 5 // four-digit minimum plus sign
	if yearDigits > 4 {
		n += yearDigits - 4
	}
	year, month, day := d.Date()
	return fmt.Sprintf("%+0*d-%02d-%02d", n, year, month, day)
}

// Format returns a textual representation of the date value formatted according
// to layout, which defines the format by showing how the reference date,
// defined to be
//     Mon, Jan 2, 2006
// would be displayed if it were the value; it serves as an example of the
// desired output.
//
// This function actually uses time.Format to format the input and can use any
// layout accepted by time.Format by extending its date to a time at
// 00:00:00.000 UTC.
//
// This function cannot currently format Date values according to the expanded
// year variant of ISO 8601; you should use Date.FormatISO to that effect.
func (d Date) Format(layout string) string {
	t := decode(d.day)
	return t.Format(layout)
}
<|endoftext|>


<commit_before>package fridge

import (
	"errors"
	"github.com/shomali11/eventbus"
	"github.com/shomali11/parallelizer"
	"time"
)

const (
	// Fresh is when an item has not passed its "Best By" duration
	Fresh = "FRESH"

	// Cold is when an item has passed its "Best By" duration but not its "Use By" one
	Cold = "COLD"

	// Expired is when an item has passed its "Use By" duration
	Expired = "EXPIRED"

	// NotFound is when an item was not found due to being removed or was never stored before
	NotFound = "NOT_FOUND"

	// Restock is when an item was restocked with a fresher one
	Restock = "RESTOCK"

	// OutOfStock is when an item needs restocking, but no restocking function was provided
	OutOfStock = "OUT_OF_STOCK"

	// Unchanged is when the restocked item is not different from the version in the cache
	Unchanged = "UNCHANGED"
)

const (
	empty                 = ""
	eventsTopic           = "fridge_events"
	invalidDurationsError = "invalid 'best by' and 'use by' durations"
)

// NewClient returns a client
func NewClient(cache Cache, options ...DefaultsOption) *Client {
	client := &Client{
		defaults: newDefaults(options...),
		dao:      newDao(cache),
		group:    parallelizer.NewGroup(),
	}

	bus := eventbus.NewClient()
	bus.Subscribe(eventsTopic, func(value interface{}) {
		event, ok := value.(*Event)
		if !ok {
			return
		}

		if client.handleEvent == nil {
			return
		}
		client.handleEvent(event)
	})

	client.bus = bus
	return client
}

// Cache is a Fridge cache interface
type Cache interface {
	// Get a value by key
	Get(key string) (string, bool, error)

	// Set a key value pair
	Set(key string, value string, timeout time.Duration) error

	// Remove a key
	Remove(key string) error

	// Ping to test connectivity
	Ping() error

	// Close to close resources
	Close() error
}

// Event is a Fridge event
type Event struct {
	Key  string
	Type string
}

// Client fridge client
type Client struct {
	defaults    *Defaults
	dao         *Dao
	bus         *eventbus.Client
	group       *parallelizer.Group
	handleEvent func(event *Event)
}

// Put an item
func (c *Client) Put(key string, value string, options ...StorageOption) error {
	storageDetails := newStorageDetails(c.defaults, options...)
	if storageDetails.BestBy > storageDetails.UseBy {
		return errors.New(invalidDurationsError)
	}

	err := c.dao.SetStorageDetails(key, storageDetails)
	if err != nil {
		return err
	}

	err = c.dao.Set(key, value, storageDetails.UseBy)
	if err != nil {
		return err
	}
	return nil
}

// Get an item
func (c *Client) Get(key string, options ...RetrievalOption) (string, bool, error) {
	retrievalDetails := newRetrievalDetails(options...)
	restock := retrievalDetails.Restock

	storageDetails, found, err := c.dao.GetStorageDetails(key)
	if err != nil {
		return empty, false, err
	}

	if !found {
		c.publish(key, NotFound)
		return empty, false, err
	}

	cachedValue, found, err := c.dao.Get(key)
	if err != nil {
		return empty, false, err
	}

	if !found {
		c.publish(key, Expired)
		return c.restock(key, cachedValue, storageDetails, restock)
	}

	now := time.Now().UTC()
	if now.Before(storageDetails.Timestamp.Add(storageDetails.BestBy)) {
		c.publish(key, Fresh)
		return cachedValue, true, nil
	}

	if now.Before(storageDetails.Timestamp.Add(storageDetails.UseBy)) {
		c.publish(key, Cold)
		if !storageDetails.Restocking {
			c.group.Add(func() {
				c.restock(key, cachedValue, storageDetails, restock)
			})
		}
		return cachedValue, true, nil
	}

	c.publish(key, Expired)
	return c.restock(key, cachedValue, storageDetails, restock)
}

// Remove an item
func (c *Client) Remove(key string) error {
	return c.dao.Remove(key)
}

// Ping pings redis
func (c *Client) Ping() error {
	return c.dao.Ping()
}

// Close closes resources
func (c *Client) Close() error {
	c.bus.Close()
	c.group.Close()
	return c.dao.Close()
}

// HandleEvent overrides the default handleEvent callback
func (c *Client) HandleEvent(handleEvent func(event *Event)) {
	c.handleEvent = handleEvent
}

func (c *Client) publish(key string, eventType string) {
	c.bus.Publish(eventsTopic, &Event{Key: key, Type: eventType})
}

func (c *Client) restock(key string, cachedValue string, storageDetails *StorageDetails, callback func() (string, error)) (string, bool, error) {
	if callback == nil {
		c.publish(key, OutOfStock)
		return empty, false, nil
	}

	storageDetails.Restocking = true
	err := c.dao.SetStorageDetails(key, storageDetails)
	if err != nil {
		return empty, false, err
	}

	freshValue, err := callback()
	if err != nil {
		storageDetails.Restocking = false
		c.dao.SetStorageDetails(key, storageDetails)
		return empty, false, err
	}

	c.publish(key, Restock)

	bestBy, useBy := storageDetails.BestBy, storageDetails.UseBy
	err = c.Put(key, freshValue, WithDurations(bestBy, useBy))
	if err != nil {
		return empty, false, err
	}

	if freshValue == cachedValue {
		c.publish(key, Unchanged)
	}
	return freshValue, true, nil
}
<commit_msg>Minor improvement<commit_after>package fridge

import (
	"errors"
	"github.com/shomali11/eventbus"
	"github.com/shomali11/parallelizer"
	"time"
)

const (
	// Fresh is when an item has not passed its "Best By" duration
	Fresh = "FRESH"

	// Cold is when an item has passed its "Best By" duration but not its "Use By" one
	Cold = "COLD"

	// Expired is when an item has passed its "Use By" duration
	Expired = "EXPIRED"

	// NotFound is when an item was not found due to being removed or was never stored before
	NotFound = "NOT_FOUND"

	// Restock is when an item was restocked with a fresher one
	Restock = "RESTOCK"

	// OutOfStock is when an item needs restocking, but no restocking function was provided
	OutOfStock = "OUT_OF_STOCK"

	// Unchanged is when the restocked item is not different from the version in the cache
	Unchanged = "UNCHANGED"
)

const (
	empty                 = ""
	eventsTopic           = "fridge_events"
	invalidDurationsError = "invalid 'best by' and 'use by' durations"
)

// NewClient returns a client
func NewClient(cache Cache, options ...DefaultsOption) *Client {
	client := &Client{
		defaults: newDefaults(options...),
		dao:      newDao(cache),
		group:    parallelizer.NewGroup(),
	}

	bus := eventbus.NewClient()
	bus.Subscribe(eventsTopic, func(value interface{}) {
		event, ok := value.(*Event)
		if !ok {
			return
		}

		if client.handleEvent == nil {
			return
		}
		client.handleEvent(event)
	})

	client.bus = bus
	return client
}

// Cache is a Fridge cache interface
type Cache interface {
	// Get a value by key
	Get(key string) (string, bool, error)

	// Set a key value pair
	Set(key string, value string, timeout time.Duration) error

	// Remove a key
	Remove(key string) error

	// Ping to test connectivity
	Ping() error

	// Close to close resources
	Close() error
}

// Event is a Fridge event
type Event struct {
	Key  string
	Type string
}

// Client fridge client
type Client struct {
	defaults    *Defaults
	dao         *Dao
	bus         *eventbus.Client
	group       *parallelizer.Group
	handleEvent func(event *Event)
}

// Put an item
func (c *Client) Put(key string, value string, options ...StorageOption) error {
	storageDetails := newStorageDetails(c.defaults, options...)
	if storageDetails.BestBy > storageDetails.UseBy {
		return errors.New(invalidDurationsError)
	}

	err := c.dao.SetStorageDetails(key, storageDetails)
	if err != nil {
		return err
	}

	err = c.dao.Set(key, value, storageDetails.UseBy)
	if err != nil {
		return err
	}
	return nil
}

// Get an item
func (c *Client) Get(key string, options ...RetrievalOption) (string, bool, error) {
	retrievalDetails := newRetrievalDetails(options...)
	restock := retrievalDetails.Restock

	storageDetails, found, err := c.dao.GetStorageDetails(key)
	if err != nil {
		return empty, false, err
	}

	if !found {
		c.publish(key, NotFound)
		return empty, false, err
	}

	cachedValue, found, err := c.dao.Get(key)
	if err != nil {
		return empty, false, err
	}

	if !found {
		c.publish(key, Expired)
		return c.restock(key, cachedValue, storageDetails, restock)
	}

	now := time.Now().UTC()
	if now.Before(storageDetails.Timestamp.Add(storageDetails.BestBy)) {
		c.publish(key, Fresh)
		return cachedValue, true, nil
	}

	if now.Before(storageDetails.Timestamp.Add(storageDetails.UseBy)) {
		c.publish(key, Cold)
		if !storageDetails.Restocking {
			go c.group.Add(func() {
				c.restock(key, cachedValue, storageDetails, restock)
			})
		}
		return cachedValue, true, nil
	}

	c.publish(key, Expired)
	return c.restock(key, cachedValue, storageDetails, restock)
}

// Remove an item
func (c *Client) Remove(key string) error {
	return c.dao.Remove(key)
}

// Ping pings redis
func (c *Client) Ping() error {
	return c.dao.Ping()
}

// Close closes resources
func (c *Client) Close() error {
	c.bus.Close()
	c.group.Close()
	return c.dao.Close()
}

// HandleEvent overrides the default handleEvent callback
func (c *Client) HandleEvent(handleEvent func(event *Event)) {
	c.handleEvent = handleEvent
}

func (c *Client) publish(key string, eventType string) {
	c.bus.Publish(eventsTopic, &Event{Key: key, Type: eventType})
}

func (c *Client) restock(key string, cachedValue string, storageDetails *StorageDetails, callback func() (string, error)) (string, bool, error) {
	if callback == nil {
		c.publish(key, OutOfStock)
		return empty, false, nil
	}

	storageDetails.Restocking = true
	err := c.dao.SetStorageDetails(key, storageDetails)
	if err != nil {
		return empty, false, err
	}

	freshValue, err := callback()
	if err != nil {
		storageDetails.Restocking = false
		c.dao.SetStorageDetails(key, storageDetails)
		return empty, false, err
	}

	c.publish(key, Restock)

	bestBy, useBy := storageDetails.BestBy, storageDetails.UseBy
	err = c.Put(key, freshValue, WithDurations(bestBy, useBy))
	if err != nil {
		return empty, false, err
	}

	if freshValue == cachedValue {
		c.publish(key, Unchanged)
	}
	return freshValue, true, nil
}
<|endoftext|>


<commit_before>package sexp

import (
	"exn"
)

type rewriteFunc func(Form) Form

// Rewrite provides convenient way to update AST.
func Rewrite(form Form, f rewriteFunc) Form {
	switch form := form.(type) {
	case *Block:
		if form := f(form); form != nil {
			return form
		}
		form.Forms = rewriteList(form.Forms, f)
	case *FormList:
		if form := f(form); form != nil {
			return form
		}
		form.Forms = rewriteList(form.Forms, f)
	case *ExprStmt:
		if form := f(form); form != nil {
			return form
		}
		form.Expr = Rewrite(form.Expr, f)

	case Bool:
		return rewriteAtom(form, f)
	case Int:
		return rewriteAtom(form, f)
	case Float:
		return rewriteAtom(form, f)
	case Str:
		return rewriteAtom(form, f)
	case Symbol:
		return rewriteAtom(form, f)
	case Var:
		return rewriteAtom(form, f)

	case *ArrayLit:
		if form := f(form); form != nil {
			return form
		}
		form.Vals = rewriteList(form.Vals, f)

	case *SparseArrayLit:
		if form := f(form); form != nil {
			return form
		}
		for i := range form.Vals {
			form.Vals[i] = Rewrite(form.Vals[i], f)
		}

	case *SliceLit:
		if form := f(form); form != nil {
			return form
		}
		form.Vals = rewriteList(form.Vals, f)

	case *ArrayIndex:
		if form := f(form); form != nil {
			return form
		}
		form.Array = Rewrite(form.Array, f)
		form.Index = Rewrite(form.Index, f)

	case *ArrayUpdate:
		if form := f(form); form != nil {
			return form
		}
		form.Array = Rewrite(form.Array, f)
		form.Index = Rewrite(form.Index, f)
		form.Expr = Rewrite(form.Expr, f)

	case *ArraySlice:
		return rewriteSpan(form, &form.Array, &form.Span, f)

	case *SliceIndex:
		if form := f(form); form != nil {
			return form
		}
		form.Slice = Rewrite(form.Slice, f)
		form.Index = Rewrite(form.Index, f)

	case *SliceUpdate:
		if form := f(form); form != nil {
			return form
		}
		form.Slice = Rewrite(form.Slice, f)
		form.Index = Rewrite(form.Index, f)
		form.Expr = Rewrite(form.Expr, f)

	case *Subslice:
		return rewriteSpan(form, &form.Slice, &form.Span, f)

	case *Substr:
		return rewriteSpan(form, &form.Str, &form.Span, f)

	case *Bind:
		if form := f(form); form != nil {
			return form
		}
		form.Init = Rewrite(form.Init, f)

	case *Rebind:
		if form := f(form); form != nil {
			return form
		}
		form.Expr = Rewrite(form.Expr, f)

	case *VarUpdate:
		if form := f(form); form != nil {
			return form
		}
		form.Expr = Rewrite(form.Expr, f)

	case *TypeAssert:
		if form := f(form); form != nil {
			return form
		}
		form.Expr = Rewrite(form.Expr, f)

	case *LispTypeAssert:
		if form := f(form); form != nil {
			return form
		}
		form.Expr = Rewrite(form.Expr, f)

	case *If:
		if form := f(form); form != nil {
			return form
		}
		form.Cond = Rewrite(form.Cond, f)
		form.Then = Rewrite(form.Then, f).(*Block)
		if form.Else != nil {
			form.Else = Rewrite(form.Else, f)
		}

	case *Return:
		if form := f(form); form != nil {
			return form
		}
		form.Results = rewriteList(form.Results, f)

	case *Repeat:
		if form := f(form); form != nil {
			return form
		}
		form.Body = Rewrite(form.Body, f).(*Block)

	case *DoTimes:
		if form := f(form); form != nil {
			return form
		}
		form.Step = Rewrite(form.Step, f)
		form.Body = Rewrite(form.Body, f).(*Block)

	case *While:
		if form := f(form); form != nil {
			return form
		}
		form.Cond = Rewrite(form.Cond, f)
		form.Body = Rewrite(form.Body, f).(*Block)

	case *LispCall:
		if form := f(form); form != nil {
			return form
		}
		form.Args = rewriteList(form.Args, f)
	case *Call:
		if form := f(form); form != nil {
			return form
		}
		form.Args = rewriteList(form.Args, f)
	case *InstrCall:
		if form := f(form); form != nil {
			return form
		}
		form.Args = rewriteList(form.Args, f)

	case *Let:
		if form := f(form); form != nil {
			return form
		}
		for i, bind := range form.Bindings {
			form.Bindings[i] = Rewrite(bind, f).(*Bind)
		}
		if form.Expr == nil {
			form.Stmt = Rewrite(form.Stmt, f)
		} else {
			form.Expr = Rewrite(form.Expr, f)
		}

	case *StructLit:
		if form := f(form); form != nil {
			return form
		}
		form.Vals = rewriteList(form.Vals, f)
	case *StructIndex:
		if form := f(form); form != nil {
			return form
		}
		form.Struct = Rewrite(form.Struct, f)
	case *StructUpdate:
		if form := f(form); form != nil {
			return form
		}
		form.Struct = Rewrite(form.Struct, f)
		form.Expr = Rewrite(form.Expr, f)

	case *And:
		if form := f(form); form != nil {
			return form
		}
		form.X = Rewrite(form.X, f)
		form.Y = Rewrite(form.Y, f)

	case *Or:
		if form := f(form); form != nil {
			return form
		}
		form.X = Rewrite(form.X, f)
		form.Y = Rewrite(form.Y, f)

	default:
		panic(exn.Logic("unexpected form: %#v", form))
	}

	return form
}

func rewriteSpan(form Form, container *Form, span *Span, f rewriteFunc) Form {
	if form := f(form); form != nil {
		return form
	}
	*container = Rewrite(*container, f)
	if span.Low != nil {
		span.Low = Rewrite(span.Low, f)
	}
	if span.High != nil {
		span.High = Rewrite(span.High, f)
	}
	return form
}

func rewriteList(forms []Form, f rewriteFunc) []Form {
	for i, form := range forms {
		forms[i] = Rewrite(form, f)
	}
	return forms
}

func rewriteUnaryOp(form Form, arg *Form, f rewriteFunc) Form {
	if form := f(form); form != nil {
		return form
	}
	*arg = Rewrite(*arg, f)
	return form
}

func rewriteBinOp(form Form, args *[2]Form, f rewriteFunc) Form {
	if form := f(form); form != nil {
		return form
	}
	args[0] = Rewrite(args[0], f)
	args[1] = Rewrite(args[1], f)
	return form
}

func rewriteAtom(form Form, f rewriteFunc) Form {
	if form := f(form); form != nil {
		return form
	}
	return form
}
<commit_msg>sexp.Rewrite refactoring<commit_after>package sexp

import (
	"exn"
)

type rewriteFunc func(Form) Form

// Rewrite provides convenient way to update AST.
func Rewrite(form Form, f rewriteFunc) Form {
	switch form := form.(type) {
	case *Block:
		return rewriteList(form, form.Forms, f)
	case *FormList:
		return rewriteList(form, form.Forms, f)
	case *ExprStmt:
		return rewrite(form, f, &form.Expr)
	case Bool:
		return rewriteAtom(form, f)
	case Int:
		return rewriteAtom(form, f)
	case Float:
		return rewriteAtom(form, f)
	case Str:
		return rewriteAtom(form, f)
	case Symbol:
		return rewriteAtom(form, f)
	case Var:
		return rewriteAtom(form, f)
	case *ArrayLit:
		return rewriteList(form, form.Vals, f)

	case *SparseArrayLit:
		if form := f(form); form != nil {
			return form
		}
		for i := range form.Vals {
			form.Vals[i] = Rewrite(form.Vals[i], f)
		}

	case *SliceLit:
		return rewriteList(form, form.Vals, f)
	case *ArrayIndex:
		return rewrite(form, f, &form.Array, &form.Index)
	case *ArrayUpdate:
		return rewrite(form, f, &form.Array, &form.Index, &form.Expr)
	case *ArraySlice:
		return rewriteSpan(form, &form.Array, &form.Span, f)
	case *SliceIndex:
		return rewrite(form, f, &form.Slice, &form.Index)
	case *SliceUpdate:
		return rewrite(form, f, &form.Slice, &form.Index, &form.Expr)
	case *Subslice:
		return rewriteSpan(form, &form.Slice, &form.Span, f)
	case *Substr:
		return rewriteSpan(form, &form.Str, &form.Span, f)
	case *Bind:
		return rewrite(form, f, &form.Init)
	case *Rebind:
		return rewrite(form, f, &form.Expr)
	case *VarUpdate:
		return rewrite(form, f, &form.Expr)
	case *TypeAssert:
		return rewrite(form, f, &form.Expr)
	case *LispTypeAssert:
		return rewrite(form, f, &form.Expr)

	case *If:
		if form := f(form); form != nil {
			return form
		}
		form.Cond = Rewrite(form.Cond, f)
		form.Then = Rewrite(form.Then, f).(*Block)
		if form.Else != nil {
			form.Else = Rewrite(form.Else, f)
		}

	case *Return:
		return rewriteList(form, form.Results, f)

	case *Repeat:
		if form := f(form); form != nil {
			return form
		}
		form.Body = Rewrite(form.Body, f).(*Block)

	case *DoTimes:
		if form := f(form); form != nil {
			return form
		}
		form.Step = Rewrite(form.Step, f)
		form.Body = Rewrite(form.Body, f).(*Block)

	case *While:
		if form := f(form); form != nil {
			return form
		}
		form.Cond = Rewrite(form.Cond, f)
		form.Body = Rewrite(form.Body, f).(*Block)

	case *LispCall:
		return rewriteList(form, form.Args, f)
	case *Call:
		return rewriteList(form, form.Args, f)
	case *InstrCall:
		return rewriteList(form, form.Args, f)

	case *Let:
		if form := f(form); form != nil {
			return form
		}
		for i, bind := range form.Bindings {
			form.Bindings[i] = Rewrite(bind, f).(*Bind)
		}
		if form.Expr == nil {
			form.Stmt = Rewrite(form.Stmt, f)
		} else {
			form.Expr = Rewrite(form.Expr, f)
		}

	case *StructLit:
		return rewriteList(form, form.Vals, f)
	case *StructIndex:
		return rewrite(form, f, &form.Struct)
	case *StructUpdate:
		return rewrite(form, f, &form.Struct, &form.Expr)
	case *And:
		return rewrite(form, f, &form.X, &form.Y)
	case *Or:
		return rewrite(form, f, &form.X, &form.Y)

	default:
		panic(exn.Logic("unexpected form: %#v", form))
	}

	return form
}

func rewriteSpan(form Form, container *Form, span *Span, f rewriteFunc) Form {
	if form := f(form); form != nil {
		return form
	}
	*container = Rewrite(*container, f)
	if span.Low != nil {
		span.Low = Rewrite(span.Low, f)
	}
	if span.High != nil {
		span.High = Rewrite(span.High, f)
	}
	return form
}

func rewriteList(form Form, forms []Form, f rewriteFunc) Form {
	if form := f(form); form != nil {
		return form
	}
	for i, form := range forms {
		forms[i] = Rewrite(form, f)
	}
	return form
}

func rewrite(form Form, f rewriteFunc, xs ...*Form) Form {
	if form := f(form); form != nil {
		return form
	}
	for _, x := range xs {
		*x = Rewrite(*x, f)
	}
	return form
}

func rewriteAtom(form Form, f rewriteFunc) Form {
	if form := f(form); form != nil {
		return form
	}
	return form
}
<|endoftext|>