<commit_before>// Copyright ©2012 The bíogo 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 pals

import (
	"code.google.com/p/biogo.interval"
	"code.google.com/p/biogo/feat"

	"fmt"
	"unsafe"
)

var duplicatePair = fmt.Errorf("pals: attempt to add duplicate feature pair to pile")

// Note Location must be comparable according to http://golang.org/ref/spec#Comparison_operators.
type PileInterval struct {
	Start, End int
	Location   feat.Feature
	Pairs      []*Pair
	overlap    int
}

func (i *PileInterval) Overlap(b interval.IntRange) bool {
	return i.End-i.overlap >= b.Start && i.Start <= b.End-i.overlap
}
func (i *PileInterval) ID() uintptr              { return uintptr(unsafe.Pointer(i)) }
func (i *PileInterval) Range() interval.IntRange { return interval.IntRange{i.Start, i.End} }

type containQuery struct {
	start, end int
	slop       int
	location   feat.Feature
}

func (q containQuery) Overlap(b interval.IntRange) bool {
	return b.Start <= q.start+q.slop && b.End >= q.end-q.slop
}
func (q containQuery) ID() uintptr              { return 0 }
func (q containQuery) Range() interval.IntRange { return interval.IntRange{q.start, q.end} }

// A Piler performs the aggregation of feature pairs according to the description in section 2.3
// of Edgar and Myers (2005) using an interval tree, giving O(nlogn) time but better space complexity
// and flexibility with feature overlap.
type Piler struct {
	intervals map[feat.Feature]*interval.IntTree
	seen      map[sp]struct{}
	overlap   int
}

type (
	sf struct {
		loc  feat.Feature
		s, e int
	}

	sp struct {
		a, b sf
	}
)

// NewPiler creates a Piler object ready for piling feature pairs.
func NewPiler(overlap int) *Piler {
	return &Piler{
		intervals: make(map[feat.Feature]*interval.IntTree),
		seen:      make(map[sp]struct{}),
		overlap:   overlap,
	}
}

// Add adds a feature pair to the piler incorporating the features into piles where appropriate.
func (p *Piler) Add(fp *Pair) error {
	a := sf{fp.A.Location(), fp.A.Start(), fp.A.End()}
	b := sf{fp.B.Location(), fp.B.Start(), fp.B.End()}
	ab, ba := sp{a, b}, sp{b, a}

	if _, ok := p.seen[ab]; ok {
		return duplicatePair
	}
	if _, ok := p.seen[ba]; ok {
		return duplicatePair
	}

	p.merge(&PileInterval{fp.A.Start(), fp.A.End(), fp.A.Location(), []*Pair{fp}, p.overlap})
	p.merge(&PileInterval{fp.B.Start(), fp.B.End(), fp.B.Location(), nil, p.overlap})
	p.seen[ab] = struct{}{}

	return nil
}

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

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

// merge merges an interval into the tree mioving location meta data from the replaced intervals
// into the new interval.
func (p *Piler) merge(pi *PileInterval) {
	var (
		f  = true
		r  []interval.IntInterface
		qi = &PileInterval{Start: pi.Start, End: pi.End}
	)
	t, ok := p.intervals[pi.Location]
	if !ok {
		t = &interval.IntTree{}
		p.intervals[pi.Location] = t
	}
	t.DoMatching(
		func(e interval.IntInterface) (done bool) {
			iv := e.(*PileInterval)
			r = append(r, e)
			pi.Pairs = append(pi.Pairs, iv.Pairs...)
			if f {
				pi.Start = min(iv.Start, pi.Start)
				f = false
			}
			pi.End = max(iv.End, pi.End)
			return
		},
		qi,
	)
	for _, d := range r {
		t.Delete(d, false)
	}
	t.Insert(pi, false)
}

// A PileFilter is used to determine whether a Pair is included in a Pile
type PileFilter func(a, b feat.Feature, pa, pb *PileInterval) bool

// Piles returns a slice of piles determined by application of the filter function f to
// the feature pairs that have been added to the piler.
func (p *Piler) Piles(f PileFilter) ([]*Pile, error) {
	var (
		pm  = make(map[*PileInterval]*Pile)
		err error
	)
	for _, t := range p.intervals {
		t.Do(
			func(e interval.IntInterface) (done bool) {
				var (
					pa = e.(*PileInterval)
					pb *PileInterval
				)
				for _, pp := range pa.Pairs {
					pb, err = p.pile(pp.B)
					if err != nil {
						return true // Terminate Do() and allow Piles() to return err.
					}

					if f != nil && !f(pp.A, pp.B, pa, pb) {
						continue
					}
					if wp, ok := pm[pa]; !ok {
						tp := &Pile{
							Loc: pa.Location, From: pa.Start, To: pa.End,
							Images: []*Pair{pp},
						}
						pp.A.(*Feature).Loc = tp
						pm[pa] = tp
					} else {
						pp.A.(*Feature).Loc = wp
						wp.Images = append(wp.Images, pp)
					}
					if wp, ok := pm[pb]; !ok {
						tp := &Pile{
							Loc: pb.Location, From: pb.Start, To: pb.End,
							Images: []*Pair{pp.Invert()},
						}
						pp.B.(*Feature).Loc = tp
						pm[pb] = tp
					} else {
						pp.B.(*Feature).Loc = wp
						wp.Images = append(wp.Images, pp.Invert())
					}
				}
				return
			},
		)
	}
	if err != nil {
		return nil, err
	}

	piles := make([]*Pile, 0, len(pm))
	for _, pile := range pm {
		piles = append(piles, pile)
	}

	return piles, nil
}

// Pile returns a Pile representation of the pile containing i.
// An error is returned if more than one pile would be returned.
func (p *Piler) Pile(q feat.Feature) (*Pile, error) {
	pi, err := p.pile(q)
	if err != nil {
		return nil, err
	}
	return &Pile{
		From:   pi.Start,
		To:     pi.End,
		Loc:    pi.Location,
		Images: pi.Pairs,
	}, nil
}

func (p *Piler) pile(q feat.Feature) (*PileInterval, error) {
	var (
		qi = containQuery{
			start:    q.Start(),
			end:      q.End(),
			location: q.Location(),
			slop:     p.overlap,
		}
		t  = p.intervals[qi.location]
		c  = 0
		pt interval.IntInterface
	)

	t.DoMatching(
		func(e interval.IntInterface) (done bool) {
			c++
			pt = e
			return
		},
		qi,
	)

	// Sanity check: no pile should overlap any other pile within overlap constraints
	// TODO: Should this be a panic?
	if c > 1 {
		return nil, fmt.Errorf("pals: internal inconsistency - too many results: %d", c)
	}

	return pt.(*PileInterval), nil
}
<commit_msg>Fix typo in private documentation<commit_after>// Copyright ©2012 The bíogo 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 pals

import (
	"code.google.com/p/biogo.interval"
	"code.google.com/p/biogo/feat"

	"fmt"
	"unsafe"
)

var duplicatePair = fmt.Errorf("pals: attempt to add duplicate feature pair to pile")

// Note Location must be comparable according to http://golang.org/ref/spec#Comparison_operators.
type PileInterval struct {
	Start, End int
	Location   feat.Feature
	Pairs      []*Pair
	overlap    int
}

func (i *PileInterval) Overlap(b interval.IntRange) bool {
	return i.End-i.overlap >= b.Start && i.Start <= b.End-i.overlap
}
func (i *PileInterval) ID() uintptr              { return uintptr(unsafe.Pointer(i)) }
func (i *PileInterval) Range() interval.IntRange { return interval.IntRange{i.Start, i.End} }

type containQuery struct {
	start, end int
	slop       int
	location   feat.Feature
}

func (q containQuery) Overlap(b interval.IntRange) bool {
	return b.Start <= q.start+q.slop && b.End >= q.end-q.slop
}
func (q containQuery) ID() uintptr              { return 0 }
func (q containQuery) Range() interval.IntRange { return interval.IntRange{q.start, q.end} }

// A Piler performs the aggregation of feature pairs according to the description in section 2.3
// of Edgar and Myers (2005) using an interval tree, giving O(nlogn) time but better space complexity
// and flexibility with feature overlap.
type Piler struct {
	intervals map[feat.Feature]*interval.IntTree
	seen      map[sp]struct{}
	overlap   int
}

type (
	sf struct {
		loc  feat.Feature
		s, e int
	}

	sp struct {
		a, b sf
	}
)

// NewPiler creates a Piler object ready for piling feature pairs.
func NewPiler(overlap int) *Piler {
	return &Piler{
		intervals: make(map[feat.Feature]*interval.IntTree),
		seen:      make(map[sp]struct{}),
		overlap:   overlap,
	}
}

// Add adds a feature pair to the piler incorporating the features into piles where appropriate.
func (p *Piler) Add(fp *Pair) error {
	a := sf{fp.A.Location(), fp.A.Start(), fp.A.End()}
	b := sf{fp.B.Location(), fp.B.Start(), fp.B.End()}
	ab, ba := sp{a, b}, sp{b, a}

	if _, ok := p.seen[ab]; ok {
		return duplicatePair
	}
	if _, ok := p.seen[ba]; ok {
		return duplicatePair
	}

	p.merge(&PileInterval{fp.A.Start(), fp.A.End(), fp.A.Location(), []*Pair{fp}, p.overlap})
	p.merge(&PileInterval{fp.B.Start(), fp.B.End(), fp.B.Location(), nil, p.overlap})
	p.seen[ab] = struct{}{}

	return nil
}

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

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

// merge merges an interval into the tree moving location meta data from the replaced intervals
// into the new interval.
func (p *Piler) merge(pi *PileInterval) {
	var (
		f  = true
		r  []interval.IntInterface
		qi = &PileInterval{Start: pi.Start, End: pi.End}
	)
	t, ok := p.intervals[pi.Location]
	if !ok {
		t = &interval.IntTree{}
		p.intervals[pi.Location] = t
	}
	t.DoMatching(
		func(e interval.IntInterface) (done bool) {
			iv := e.(*PileInterval)
			r = append(r, e)
			pi.Pairs = append(pi.Pairs, iv.Pairs...)
			if f {
				pi.Start = min(iv.Start, pi.Start)
				f = false
			}
			pi.End = max(iv.End, pi.End)
			return
		},
		qi,
	)
	for _, d := range r {
		t.Delete(d, false)
	}
	t.Insert(pi, false)
}

// A PileFilter is used to determine whether a Pair is included in a Pile
type PileFilter func(a, b feat.Feature, pa, pb *PileInterval) bool

// Piles returns a slice of piles determined by application of the filter function f to
// the feature pairs that have been added to the piler.
func (p *Piler) Piles(f PileFilter) ([]*Pile, error) {
	var (
		pm  = make(map[*PileInterval]*Pile)
		err error
	)
	for _, t := range p.intervals {
		t.Do(
			func(e interval.IntInterface) (done bool) {
				var (
					pa = e.(*PileInterval)
					pb *PileInterval
				)
				for _, pp := range pa.Pairs {
					pb, err = p.pile(pp.B)
					if err != nil {
						return true // Terminate Do() and allow Piles() to return err.
					}

					if f != nil && !f(pp.A, pp.B, pa, pb) {
						continue
					}
					if wp, ok := pm[pa]; !ok {
						tp := &Pile{
							Loc: pa.Location, From: pa.Start, To: pa.End,
							Images: []*Pair{pp},
						}
						pp.A.(*Feature).Loc = tp
						pm[pa] = tp
					} else {
						pp.A.(*Feature).Loc = wp
						wp.Images = append(wp.Images, pp)
					}
					if wp, ok := pm[pb]; !ok {
						tp := &Pile{
							Loc: pb.Location, From: pb.Start, To: pb.End,
							Images: []*Pair{pp.Invert()},
						}
						pp.B.(*Feature).Loc = tp
						pm[pb] = tp
					} else {
						pp.B.(*Feature).Loc = wp
						wp.Images = append(wp.Images, pp.Invert())
					}
				}
				return
			},
		)
	}
	if err != nil {
		return nil, err
	}

	piles := make([]*Pile, 0, len(pm))
	for _, pile := range pm {
		piles = append(piles, pile)
	}

	return piles, nil
}

// Pile returns a Pile representation of the pile containing i.
// An error is returned if more than one pile would be returned.
func (p *Piler) Pile(q feat.Feature) (*Pile, error) {
	pi, err := p.pile(q)
	if err != nil {
		return nil, err
	}
	return &Pile{
		From:   pi.Start,
		To:     pi.End,
		Loc:    pi.Location,
		Images: pi.Pairs,
	}, nil
}

func (p *Piler) pile(q feat.Feature) (*PileInterval, error) {
	var (
		qi = containQuery{
			start:    q.Start(),
			end:      q.End(),
			location: q.Location(),
			slop:     p.overlap,
		}
		t  = p.intervals[qi.location]
		c  = 0
		pt interval.IntInterface
	)

	t.DoMatching(
		func(e interval.IntInterface) (done bool) {
			c++
			pt = e
			return
		},
		qi,
	)

	// Sanity check: no pile should overlap any other pile within overlap constraints
	// TODO: Should this be a panic?
	if c > 1 {
		return nil, fmt.Errorf("pals: internal inconsistency - too many results: %d", c)
	}

	return pt.(*PileInterval), nil
}
<|endoftext|>


<commit_before>
package compiler

import (
  "fmt"
  "github.com/lestrrat/go-xslate/parser"
  "github.com/lestrrat/go-xslate/vm"
)

// Compiler is the interface to objects that can convert AST trees to
// actual Xslate Virtual Machine bytecode (see vm.ByteCode)
type Compiler interface {
  Compile(* parser.AST) (*vm.ByteCode, error)
}

type context struct {
  ByteCode *vm.ByteCode
}

func (ctx *context) AppendOp(o vm.OpType, args ...interface {}) *vm.Op {
  return ctx.ByteCode.AppendOp(o, args...)
}

// BasicCompiler is the default compiler used by Xslate
type BasicCompiler struct {}

// New creates a new BasicCompiler instance
func New() *BasicCompiler {
  return &BasicCompiler {}
}

// Compile satisfies the compiler.Compiler interface. It accepts an AST
// created by parser.Parser, and returns vm.ByteCode or an error
func (c *BasicCompiler) Compile(ast *parser.AST) (*vm.ByteCode, error) {
  ctx := &context {
    ByteCode: vm.NewByteCode(),
  }
  for _, n := range ast.Root.Nodes {
    c.compile(ctx, n)
  }

  // When we're done compiling, always append an END op
  ctx.ByteCode.AppendOp(vm.TXOPEnd)

  return ctx.ByteCode, nil
}

func (c *BasicCompiler) compile(ctx *context, n parser.Node) {
  switch n.Type() {
  case parser.NodeText:
    // XXX probably not true all the time
    ctx.AppendOp(vm.TXOPLiteral, n.(*parser.TextNode).Text)
  case parser.NodeFetchSymbol:
    ctx.AppendOp(vm.TXOPFetchSymbol, n.(*parser.TextNode).Text)
  case parser.NodeFetchField:
    ffnode := n.(*parser.FetchFieldNode)
    c.compile(ctx, ffnode.Container)
    ctx.AppendOp(vm.TXOPFetchFieldSymbol, ffnode.FieldName)
  case parser.NodeLocalVar:
    l := n.(*parser.LocalVarNode)
    ctx.AppendOp(vm.TXOPLoadLvar, l.Offset)
  case parser.NodeAssignment:
    c.compile(ctx, n.(*parser.AssignmentNode).Expression)
    ctx.AppendOp(vm.TXOPSaveToLvar, 0) // XXX this 0 must be pre-computed
  case parser.NodePrint:
    c.compile(ctx, n.(*parser.ListNode).Nodes[0])
    ctx.AppendOp(vm.TXOPPrint)
  case parser.NodePrintRaw:
    c.compile(ctx, n.(*parser.ListNode).Nodes[0])
    ctx.AppendOp(vm.TXOPPrintRaw)
  case parser.NodeForeach:
    ctx.AppendOp(vm.TXOPPushmark)
    c.compile(ctx, n.(*parser.ForeachNode).List)
    ctx.AppendOp(vm.TXOPForStart, 0)
    ctx.AppendOp(vm.TXOPLiteral, 0)
    iter := ctx.AppendOp(vm.TXOPForIter, 0)
    pos  := ctx.ByteCode.Len()

    children := n.(*parser.ForeachNode).Nodes
    for _, v := range children {
      c.compile(ctx, v)
    }

    ctx.AppendOp(vm.TXOPGoto, -1 * (ctx.ByteCode.Len() - pos + 2))
    iter.SetArg(ctx.ByteCode.Len() - pos + 1)
    ctx.AppendOp(vm.TXOPPopmark)
  case parser.NodeIf:
    x := n.(*parser.IfNode)
    ctx.AppendOp(vm.TXOPPushmark)
    c.compile(ctx, x.BooleanExpression)
    ifop := ctx.AppendOp(vm.TXOPAnd, 0)
    pos := ctx.ByteCode.Len()

    var elseNode parser.Node
    children := x.ListNode.Nodes
    for _, child := range children {
      if child.Type() == parser.NodeElse {
        elseNode = child
      } else {
        c.compile(ctx, child)
      }
    }
    if elseNode == nil {
      ifop.SetArg(ctx.ByteCode.Len() - pos + 1)
    } else {
      // If we have an else, we need to put this AFTER the goto
      // that's generated by else
      ifop.SetArg(ctx.ByteCode.Len() - pos + 2)
      c.compile(ctx, elseNode)
    }

    ctx.AppendOp(vm.TXOPPopmark)
  case parser.NodeElse:
    gotoOp := ctx.AppendOp(vm.TXOPGoto, 0)
    pos := ctx.ByteCode.Len()
    for _, child := range n.(*parser.ElseNode).ListNode.Nodes {
      c.compile(ctx, child)
    }
    gotoOp.SetArg(ctx.ByteCode.Len() - pos + 1)
  case parser.NodeMakeArray:
    x := n.(*parser.UnaryNode)
    c.compile(ctx, x.Child)
    ctx.AppendOp(vm.TXOPMakeArray)
  case parser.NodeRange:
    x := n.(*parser.RangeNode)
    ctx.AppendOp(vm.TXOPLiteral, x.Start)
    ctx.AppendOp(vm.TXOPMoveToSb)
    ctx.AppendOp(vm.TXOPLiteral, x.End)
    ctx.AppendOp(vm.TXOPRange)
  case parser.NodeInt:
    x := n.(*parser.NumberNode)
    ctx.AppendOp(vm.TXOPLiteral, x.Value.Int())
  case parser.NodeList:
    x := n.(*parser.ListNode)
    for _, v := range x.Nodes {
      c.compile(ctx, v)
      if v.Type() != parser.NodeRange {
        ctx.AppendOp(vm.TXOPPush)
      }
    }
  case parser.NodeFunCall:
    x := n.(*parser.FunCallNode)

    for _, child := range x.Args.Nodes {
      c.compile(ctx, child)
      ctx.AppendOp(vm.TXOPPush)
    }

    c.compile(ctx, x.Invocant)
    ctx.AppendOp(vm.TXOPFunCall)
  case parser.NodeMethodCall:
    x := n.(*parser.MethodCallNode)

    c.compile(ctx, x.Invocant)
    ctx.AppendOp(vm.TXOPPush)
    ctx.AppendOp(vm.TXOPPushmark)
    for _, child := range x.Args.Nodes {
      c.compile(ctx, child)
      ctx.AppendOp(vm.TXOPPush)
    }
    ctx.AppendOp(vm.TXOPMethodCall, x.MethodName)
    ctx.AppendOp(vm.TXOPPopmark)
  case parser.NodeWrapper:
    x := n.(*parser.WrapperNode)
    ctx.AppendOp(vm.TXOPPushOutput)
    ctx.AppendOp(vm.TXOPNewOutput)
    for _, v := range x.ListNode.Nodes {
      c.compile(ctx, v)
    }
    ctx.AppendOp(vm.TXOPPopOutput)

    ctx.AppendOp(vm.TXOPPush)
    // Arguments to include (WITH foo = "bar") need to be evaulated
    // in the OUTER context, but the variables need to be set in the
    // include context
    c.compileAssignmentNodes(ctx, x.AssignmentNodes)
    ctx.AppendOp(vm.TXOPPop)
    ctx.AppendOp(vm.TXOPPushmark)
    ctx.AppendOp(vm.TXOPWrapper, x.WrapperName)
    ctx.AppendOp(vm.TXOPPopmark)
  case parser.NodeInclude:
    x := n.(*parser.IncludeNode)

    c.compile(ctx, x.IncludeTarget)
    ctx.AppendOp(vm.TXOPPush)
    // Arguments to include (WITH foo = "bar") need to be evaulated
    // in the OUTER context, but the variables need to be set in the
    // include context
    c.compileAssignmentNodes(ctx, x.AssignmentNodes)
    ctx.AppendOp(vm.TXOPPop)
    ctx.AppendOp(vm.TXOPPushmark)
    ctx.AppendOp(vm.TXOPInclude)
    ctx.AppendOp(vm.TXOPPopmark)
  case parser.NodeGroup:
    c.compile(ctx, n.(*parser.UnaryNode).Child)
  case parser.NodeEquals, parser.NodeNotEquals, parser.NodeLT, parser.NodeGT:
    x := n.(*parser.BinaryNode)

    c.compileBinaryOperands(ctx, x)
    switch n.Type() {
    case parser.NodeEquals:
      ctx.AppendOp(vm.TXOPEquals)
    case parser.NodeNotEquals:
      ctx.AppendOp(vm.TXOPNotEquals)
    case parser.NodeLT:
      ctx.AppendOp(vm.TXOPLessThan)
    case parser.NodeGT:
      ctx.AppendOp(vm.TXOPGreaterThan)
    default:
      panic("Unknown operator")
    }
  case parser.NodePlus, parser.NodeMinus, parser.NodeMul, parser.NodeDiv:
    x := n.(*parser.BinaryNode)

    c.compileBinaryOperands(ctx, x)
    switch n.Type() {
    case parser.NodePlus:
      ctx.AppendOp(vm.TXOPAdd)
    case parser.NodeMinus:
      ctx.AppendOp(vm.TXOPSub)
    case parser.NodeMul:
      ctx.AppendOp(vm.TXOPMul)
    case parser.NodeDiv:
      ctx.AppendOp(vm.TXOPDiv)
    default:
      panic("Unknown arithmetic")
    }
  case parser.NodeFilter:
    x := n.(*parser.FilterNode)

    c.compile(ctx, x.Child)
    ctx.AppendOp(vm.TXOPFilter, x.Name)
  default:
    fmt.Printf("Unknown node: %s\n", n.Type())
  }
}

func (c *BasicCompiler) compileBinaryOperands(ctx *context, x *parser.BinaryNode) {
  if x.Right.Type() == parser.NodeGroup {
    // Grouped node
    c.compile(ctx, x.Right)
    ctx.AppendOp(vm.TXOPPush)
    c.compile(ctx, x.Left)
    ctx.AppendOp(vm.TXOPMoveToSb)
    ctx.AppendOp(vm.TXOPPop)
  } else {
    c.compile(ctx, x.Left)
    ctx.AppendOp(vm.TXOPMoveToSb)
    c.compile(ctx, x.Right)
  }
}

func (c *BasicCompiler) compileAssignmentNodes(ctx *context, assignnodes []parser.Node) {
  if len(assignnodes) > 0 {
    ctx.AppendOp(vm.TXOPPushmark)
    for _, nv := range assignnodes {
      v := nv.(*parser.AssignmentNode)
      ctx.AppendOp(vm.TXOPLiteral, v.Assignee.Name)
      ctx.AppendOp(vm.TXOPPush)
      c.compile(ctx, v.Expression)
      ctx.AppendOp(vm.TXOPPush)
    }
    ctx.AppendOp(vm.TXOPMakeHash)
    ctx.AppendOp(vm.TXOPMoveToSb)
    ctx.AppendOp(vm.TXOPPopmark)
  }
}
<commit_msg>tweaks<commit_after>
package compiler

import (
  "fmt"
  "github.com/lestrrat/go-xslate/parser"
  "github.com/lestrrat/go-xslate/vm"
)

// Compiler is the interface to objects that can convert AST trees to
// actual Xslate Virtual Machine bytecode (see vm.ByteCode)
type Compiler interface {
  Compile(* parser.AST) (*vm.ByteCode, error)
}

type context struct {
  ByteCode *vm.ByteCode
}

// AppendOp creates and appends a new op to the current set of ByteCode
func (ctx *context) AppendOp(o vm.OpType, args ...interface {}) *vm.Op {
  return ctx.ByteCode.AppendOp(o, args...)
}

// BasicCompiler is the default compiler used by Xslate
type BasicCompiler struct {}

// New creates a new BasicCompiler instance
func New() *BasicCompiler {
  return &BasicCompiler {}
}

// Compile satisfies the compiler.Compiler interface. It accepts an AST
// created by parser.Parser, and returns vm.ByteCode or an error
func (c *BasicCompiler) Compile(ast *parser.AST) (*vm.ByteCode, error) {
  ctx := &context {
    ByteCode: vm.NewByteCode(),
  }
  for _, n := range ast.Root.Nodes {
    c.compile(ctx, n)
  }

  // When we're done compiling, always append an END op
  ctx.ByteCode.AppendOp(vm.TXOPEnd)

  return ctx.ByteCode, nil
}

func (c *BasicCompiler) compile(ctx *context, n parser.Node) {
  switch n.Type() {
  case parser.NodeText:
    // XXX probably not true all the time
    ctx.AppendOp(vm.TXOPLiteral, n.(*parser.TextNode).Text)
  case parser.NodeFetchSymbol:
    ctx.AppendOp(vm.TXOPFetchSymbol, n.(*parser.TextNode).Text)
  case parser.NodeFetchField:
    ffnode := n.(*parser.FetchFieldNode)
    c.compile(ctx, ffnode.Container)
    ctx.AppendOp(vm.TXOPFetchFieldSymbol, ffnode.FieldName)
  case parser.NodeLocalVar:
    l := n.(*parser.LocalVarNode)
    ctx.AppendOp(vm.TXOPLoadLvar, l.Offset)
  case parser.NodeAssignment:
    c.compile(ctx, n.(*parser.AssignmentNode).Expression)
    ctx.AppendOp(vm.TXOPSaveToLvar, 0) // XXX this 0 must be pre-computed
  case parser.NodePrint:
    c.compile(ctx, n.(*parser.ListNode).Nodes[0])
    ctx.AppendOp(vm.TXOPPrint)
  case parser.NodePrintRaw:
    c.compile(ctx, n.(*parser.ListNode).Nodes[0])
    ctx.AppendOp(vm.TXOPPrintRaw)
  case parser.NodeForeach:
    ctx.AppendOp(vm.TXOPPushmark)
    c.compile(ctx, n.(*parser.ForeachNode).List)
    ctx.AppendOp(vm.TXOPForStart, 0)
    ctx.AppendOp(vm.TXOPLiteral, 0)
    iter := ctx.AppendOp(vm.TXOPForIter, 0)
    pos  := ctx.ByteCode.Len()

    children := n.(*parser.ForeachNode).Nodes
    for _, v := range children {
      c.compile(ctx, v)
    }

    ctx.AppendOp(vm.TXOPGoto, -1 * (ctx.ByteCode.Len() - pos + 2))
    iter.SetArg(ctx.ByteCode.Len() - pos + 1)
    ctx.AppendOp(vm.TXOPPopmark)
  case parser.NodeIf:
    c.compileIf(ctx, n)
  case parser.NodeElse:
    gotoOp := ctx.AppendOp(vm.TXOPGoto, 0)
    pos := ctx.ByteCode.Len()
    for _, child := range n.(*parser.ElseNode).ListNode.Nodes {
      c.compile(ctx, child)
    }
    gotoOp.SetArg(ctx.ByteCode.Len() - pos + 1)
  case parser.NodeMakeArray:
    x := n.(*parser.UnaryNode)
    c.compile(ctx, x.Child)
    ctx.AppendOp(vm.TXOPMakeArray)
  case parser.NodeRange:
    x := n.(*parser.RangeNode)
    ctx.AppendOp(vm.TXOPLiteral, x.Start)
    ctx.AppendOp(vm.TXOPMoveToSb)
    ctx.AppendOp(vm.TXOPLiteral, x.End)
    ctx.AppendOp(vm.TXOPRange)
  case parser.NodeInt:
    x := n.(*parser.NumberNode)
    ctx.AppendOp(vm.TXOPLiteral, x.Value.Int())
  case parser.NodeList:
    x := n.(*parser.ListNode)
    for _, v := range x.Nodes {
      c.compile(ctx, v)
      if v.Type() != parser.NodeRange {
        ctx.AppendOp(vm.TXOPPush)
      }
    }
  case parser.NodeFunCall:
    x := n.(*parser.FunCallNode)

    for _, child := range x.Args.Nodes {
      c.compile(ctx, child)
      ctx.AppendOp(vm.TXOPPush)
    }

    c.compile(ctx, x.Invocant)
    ctx.AppendOp(vm.TXOPFunCall)
  case parser.NodeMethodCall:
    x := n.(*parser.MethodCallNode)

    c.compile(ctx, x.Invocant)
    ctx.AppendOp(vm.TXOPPush)
    ctx.AppendOp(vm.TXOPPushmark)
    for _, child := range x.Args.Nodes {
      c.compile(ctx, child)
      ctx.AppendOp(vm.TXOPPush)
    }
    ctx.AppendOp(vm.TXOPMethodCall, x.MethodName)
    ctx.AppendOp(vm.TXOPPopmark)
  case parser.NodeWrapper:
    x := n.(*parser.WrapperNode)
    ctx.AppendOp(vm.TXOPPushOutput)
    ctx.AppendOp(vm.TXOPNewOutput)
    for _, v := range x.ListNode.Nodes {
      c.compile(ctx, v)
    }
    ctx.AppendOp(vm.TXOPPopOutput)

    ctx.AppendOp(vm.TXOPPush)
    // Arguments to include (WITH foo = "bar") need to be evaulated
    // in the OUTER context, but the variables need to be set in the
    // include context
    c.compileAssignmentNodes(ctx, x.AssignmentNodes)
    ctx.AppendOp(vm.TXOPPop)
    ctx.AppendOp(vm.TXOPPushmark)
    ctx.AppendOp(vm.TXOPWrapper, x.WrapperName)
    ctx.AppendOp(vm.TXOPPopmark)
  case parser.NodeInclude:
    x := n.(*parser.IncludeNode)

    c.compile(ctx, x.IncludeTarget)
    ctx.AppendOp(vm.TXOPPush)
    // Arguments to include (WITH foo = "bar") need to be evaulated
    // in the OUTER context, but the variables need to be set in the
    // include context
    c.compileAssignmentNodes(ctx, x.AssignmentNodes)
    ctx.AppendOp(vm.TXOPPop)
    ctx.AppendOp(vm.TXOPPushmark)
    ctx.AppendOp(vm.TXOPInclude)
    ctx.AppendOp(vm.TXOPPopmark)
  case parser.NodeGroup:
    c.compile(ctx, n.(*parser.UnaryNode).Child)
  case parser.NodeEquals, parser.NodeNotEquals, parser.NodeLT, parser.NodeGT:
    x := n.(*parser.BinaryNode)

    c.compileBinaryOperands(ctx, x)
    switch n.Type() {
    case parser.NodeEquals:
      ctx.AppendOp(vm.TXOPEquals)
    case parser.NodeNotEquals:
      ctx.AppendOp(vm.TXOPNotEquals)
    case parser.NodeLT:
      ctx.AppendOp(vm.TXOPLessThan)
    case parser.NodeGT:
      ctx.AppendOp(vm.TXOPGreaterThan)
    default:
      panic("Unknown operator")
    }
  case parser.NodePlus, parser.NodeMinus, parser.NodeMul, parser.NodeDiv:
    x := n.(*parser.BinaryNode)

    c.compileBinaryOperands(ctx, x)
    switch n.Type() {
    case parser.NodePlus:
      ctx.AppendOp(vm.TXOPAdd)
    case parser.NodeMinus:
      ctx.AppendOp(vm.TXOPSub)
    case parser.NodeMul:
      ctx.AppendOp(vm.TXOPMul)
    case parser.NodeDiv:
      ctx.AppendOp(vm.TXOPDiv)
    default:
      panic("Unknown arithmetic")
    }
  case parser.NodeFilter:
    x := n.(*parser.FilterNode)

    c.compile(ctx, x.Child)
    ctx.AppendOp(vm.TXOPFilter, x.Name)
  default:
    fmt.Printf("Unknown node: %s\n", n.Type())
  }
}

func (c *BasicCompiler) compileIf(ctx *context, n parser.Node) {
  x := n.(*parser.IfNode)
  ctx.AppendOp(vm.TXOPPushmark)
  c.compile(ctx, x.BooleanExpression)
  ifop := ctx.AppendOp(vm.TXOPAnd, 0)
  pos := ctx.ByteCode.Len()

  var elseNode parser.Node
  children := x.ListNode.Nodes
  for _, child := range children {
    if child.Type() == parser.NodeElse {
      elseNode = child
    } else {
      c.compile(ctx, child)
    }
  }

  if elseNode == nil {
    ifop.SetArg(ctx.ByteCode.Len() - pos + 1)
  } else {
    // If we have an else, we need to put this AFTER the goto
    // that's generated by else
    ifop.SetArg(ctx.ByteCode.Len() - pos + 2)
    c.compile(ctx, elseNode)
  }
  ctx.AppendOp(vm.TXOPPopmark)
}

func (c *BasicCompiler) compileBinaryOperands(ctx *context, x *parser.BinaryNode) {
  if x.Right.Type() == parser.NodeGroup {
    // Grouped node
    c.compile(ctx, x.Right)
    ctx.AppendOp(vm.TXOPPush)
    c.compile(ctx, x.Left)
    ctx.AppendOp(vm.TXOPMoveToSb)
    ctx.AppendOp(vm.TXOPPop)
  } else {
    c.compile(ctx, x.Left)
    ctx.AppendOp(vm.TXOPMoveToSb)
    c.compile(ctx, x.Right)
  }
}

func (c *BasicCompiler) compileAssignmentNodes(ctx *context, assignnodes []parser.Node) {
  if len(assignnodes) <= 0 {
    return
  }
  ctx.AppendOp(vm.TXOPPushmark)
  for _, nv := range assignnodes {
    v := nv.(*parser.AssignmentNode)
    ctx.AppendOp(vm.TXOPLiteral, v.Assignee.Name)
    ctx.AppendOp(vm.TXOPPush)
    c.compile(ctx, v.Expression)
    ctx.AppendOp(vm.TXOPPush)
  }
  ctx.AppendOp(vm.TXOPMakeHash)
  ctx.AppendOp(vm.TXOPMoveToSb)
  ctx.AppendOp(vm.TXOPPopmark)
}
<|endoftext|>


<commit_before>package anagrambler_test

import (
	"testing"

	"io/ioutil"
	"strings"

	"github.com/RyanEdwardHall/anagrambler"
)

type dataItem struct {
	dict string
	input string
	filter string
	anagrams int
}

// Test data and test fixtures
var (
	testData = []dataItem {
		{"go-dict.txt", "honorificabilitudinitatibus", "", 9083},
		{"go-dict.txt", "honorificabilitudinitatibus", "bus", 34},
		{"go-dict.txt", "pneumonoultramicroscopicsilicovolcanoconiosis", "", 26035},
		{"go-dict.txt", "pneumonoultramicroscopicsilicovolcanoconiosis", "ultra", 24},
		{"go-dict.txt", "Lopadotemachoselachogaleokranioleipsanodrimhypotrimmatosilphioparaomelitokatakechymenokichlepikossyphophattoperisteralektryonoptekephalliokigklopeleiolagoiosiraiobaphetraganopterygon", "", 112436},
		{"go-dict.txt", "Lopadotemachoselachogaleokranioleipsanodrimhypotrimmatosilphioparaomelitokatakechymenokichlepikossyphophattoperisteralektryonoptekephalliokigklopeleiolagoiosiraiobaphetraganopterygon", "pet", 342},
	}
	testTrie = anagrambler.NewNode()
)

func init() {
	anagrambler.LoadDict(testTrie, testData[0].dict)
}

func testAnagramCount(t *testing.T, d dataItem) {
	results := anagrambler.Search(testTrie, d.input, d.filter)

	if len(results) == d.anagrams {
		t.Logf("Success: found all %d expected anagrams for '%s' with filter '%s'\n", d.anagrams, d.input, d.filter)
	} else {
		t.Error("Expected", d.anagrams, "words, got ", len(results))
	}
}

func benchmarkFillTrie(b *testing.B, dictPath string) {
	data, err := ioutil.ReadFile(dictPath)

	if err != nil {
		b.Error("Could not load dictionary", dictPath, err)
	}

	words := strings.Split(string(data), "\n")
	words = words[:len(words)-1]

	for counter := 0; counter < b.N; counter++ {
		trie := anagrambler.NewNode()

		for _, word := range words {
			anagrambler.AddWord(trie, word)
		}
	}
}

func benchmarkSearch(b *testing.B, d dataItem) {
	for counter := 0; counter < b.N; counter++ {
		anagrambler.Search(testTrie, d.input, d.filter)
	}
}

func TestAnagramCountShort(t *testing.T) { testAnagramCount(t, testData[0]) }
func TestAnagramCountShortFiltered(t *testing.T) { testAnagramCount(t, testData[1]) }
func TestAnagramCountMedium(t *testing.T) { testAnagramCount(t, testData[2]) }
func TestAnagramCountMediumFiltered(t *testing.T) { testAnagramCount(t, testData[3]) }
func TestAngaramCountLong(t *testing.T) { testAnagramCount(t, testData[4]) }
func TestAngaramCountLongFilter(t *testing.T) { testAnagramCount(t, testData[5]) }

func BenchmarkFillTrie(b *testing.B) { benchmarkFillTrie(b, testData[0].dict) }

func BenchmarkSearchShort(b *testing.B) { benchmarkSearch(b, testData[0]) }
func BenchmarkSearchShortFiltered(b *testing.B) { benchmarkSearch(b, testData[1]) }
func BenchmarkSearchMedium(b *testing.B) { benchmarkSearch(b, testData[2]) }
func BenchmarkSearchMediumFiltered(b *testing.B) { benchmarkSearch(b, testData[3]) }
func BenchmarkSearchLong(b *testing.B) { benchmarkSearch(b, testData[4]) }
func BenchmarkSearchLongFiltered(b *testing.B) { benchmarkSearch(b, testData[5]) }
<commit_msg>Run gofmt on _test file<commit_after>package anagrambler_test

import (
	"testing"

	"io/ioutil"
	"strings"

	"github.com/RyanEdwardHall/anagrambler"
)

type dataItem struct {
	dict     string
	input    string
	filter   string
	anagrams int
}

// Test data and test fixtures
var (
	testData = []dataItem{
		{"go-dict.txt", "honorificabilitudinitatibus", "", 9083},
		{"go-dict.txt", "honorificabilitudinitatibus", "bus", 34},
		{"go-dict.txt", "pneumonoultramicroscopicsilicovolcanoconiosis", "", 26035},
		{"go-dict.txt", "pneumonoultramicroscopicsilicovolcanoconiosis", "ultra", 24},
		{"go-dict.txt", "Lopadotemachoselachogaleokranioleipsanodrimhypotrimmatosilphioparaomelitokatakechymenokichlepikossyphophattoperisteralektryonoptekephalliokigklopeleiolagoiosiraiobaphetraganopterygon", "", 112436},
		{"go-dict.txt", "Lopadotemachoselachogaleokranioleipsanodrimhypotrimmatosilphioparaomelitokatakechymenokichlepikossyphophattoperisteralektryonoptekephalliokigklopeleiolagoiosiraiobaphetraganopterygon", "pet", 342},
	}
	testTrie = anagrambler.NewNode()
)

func init() {
	anagrambler.LoadDict(testTrie, testData[0].dict)
}

func testAnagramCount(t *testing.T, d dataItem) {
	results := anagrambler.Search(testTrie, d.input, d.filter)

	if len(results) == d.anagrams {
		t.Logf("Success: found all %d expected anagrams for '%s' with filter '%s'\n", d.anagrams, d.input, d.filter)
	} else {
		t.Error("Expected", d.anagrams, "words, got ", len(results))
	}
}

func benchmarkFillTrie(b *testing.B, dictPath string) {
	data, err := ioutil.ReadFile(dictPath)

	if err != nil {
		b.Error("Could not load dictionary", dictPath, err)
	}

	words := strings.Split(string(data), "\n")
	words = words[:len(words)-1]

	for counter := 0; counter < b.N; counter++ {
		trie := anagrambler.NewNode()

		for _, word := range words {
			anagrambler.AddWord(trie, word)
		}
	}
}

func benchmarkSearch(b *testing.B, d dataItem) {
	for counter := 0; counter < b.N; counter++ {
		anagrambler.Search(testTrie, d.input, d.filter)
	}
}

// Tests for verifying correct output (i.e. correct number of anagrams for given input).
func TestAnagramCountShort(t *testing.T)          { testAnagramCount(t, testData[0]) }
func TestAnagramCountShortFiltered(t *testing.T)  { testAnagramCount(t, testData[1]) }
func TestAnagramCountMedium(t *testing.T)         { testAnagramCount(t, testData[2]) }
func TestAnagramCountMediumFiltered(t *testing.T) { testAnagramCount(t, testData[3]) }
func TestAngaramCountLong(t *testing.T)           { testAnagramCount(t, testData[4]) }
func TestAngaramCountLongFilter(t *testing.T)     { testAnagramCount(t, testData[5]) }

// Benchmarks for loading a dictionary file into a trie.
func BenchmarkFillTrie(b *testing.B) { benchmarkFillTrie(b, testData[0].dict) }

// Benchmarks for searching for anagrams.
func BenchmarkSearchShort(b *testing.B)          { benchmarkSearch(b, testData[0]) }
func BenchmarkSearchShortFiltered(b *testing.B)  { benchmarkSearch(b, testData[1]) }
func BenchmarkSearchMedium(b *testing.B)         { benchmarkSearch(b, testData[2]) }
func BenchmarkSearchMediumFiltered(b *testing.B) { benchmarkSearch(b, testData[3]) }
func BenchmarkSearchLong(b *testing.B)           { benchmarkSearch(b, testData[4]) }
func BenchmarkSearchLongFiltered(b *testing.B)   { benchmarkSearch(b, testData[5]) }
<|endoftext|>


<commit_before>package tfmt

import (
	"fmt"
	"strconv"
)

// Color provides ANSI color support. As Color implements Stringer you can
// simply add a color to your print commands like fmt.Print(Red,"Hello world").
type Color int

const (
	// ResetColors resets foreground and background color
	ResetColors = Color(0)
	// NoColor is used to not change the current color
	NoColor = Color(-1)
	// Black color
	Black = Color(30)
	// Red color
	Red = Color(31)
	// Green color
	Green = Color(32)
	// Yellow color
	Yellow = Color(33)
	// Blue color
	Blue = Color(34)
	// Purple color
	Purple = Color(35)
	// Cyan color
	Cyan = Color(36)
	// Gray color
	Gray = Color(37)
	// DarkGray color (bold Black)
	DarkGray = Color(-30)
	// BrightRed color (bold Red)
	BrightRed = Color(-31)
	// BrightGreen color (bold Green)
	BrightGreen = Color(-32)
	// BrightYellow color (bold Yellow)
	BrightYellow = Color(-33)
	// BrightBlue color (bold Blue)
	BrightBlue = Color(-34)
	// BrightPurple color (bold Purple)
	BrightPurple = Color(-35)
	// BrightCyan color (bold Cyan)
	BrightCyan = Color(-36)
	// White color (bold Gray)
	White = Color(-37)
)

// Colorize returns a colored string with the given colors.
func Colorize(c Color, b BackgroundColor, text string) string {
	return fmt.Sprintf("%s%s%s%s", ResetColors, c, b, text)
}

// Colorizef returns a colored, formatted string with the given colors.
func Colorizef(c Color, b BackgroundColor, format string, args ...interface{}) string {
	return fmt.Sprintf("%s%s%s"+format, ResetColors, c, b, args)
}

// String implements the stringer interface for color
func (c Color) String() string {
	if c == NoColor {
		return ""
	}

	if int(c) < 0 {
		return "\x1b[1m\x1b[" + strconv.Itoa(int(c)) + "m\x1b[22m"
	}
	return "\x1b[" + strconv.Itoa(int(c)) + "m"
}
<commit_msg>Fixed bright color code<commit_after>package tfmt

import (
	"fmt"
	"strconv"
)

// Color provides ANSI color support. As Color implements Stringer you can
// simply add a color to your print commands like fmt.Print(Red,"Hello world").
type Color int

const (
	// ResetColors resets foreground and background color
	ResetColors = Color(0)
	// NoColor is used to not change the current color
	NoColor = Color(-1)
	// Black color
	Black = Color(30)
	// Red color
	Red = Color(31)
	// Green color
	Green = Color(32)
	// Yellow color
	Yellow = Color(33)
	// Blue color
	Blue = Color(34)
	// Purple color
	Purple = Color(35)
	// Cyan color
	Cyan = Color(36)
	// Gray color
	Gray = Color(37)
	// DarkGray color (bold Black)
	DarkGray = Color(-30)
	// BrightRed color (bold Red)
	BrightRed = Color(-31)
	// BrightGreen color (bold Green)
	BrightGreen = Color(-32)
	// BrightYellow color (bold Yellow)
	BrightYellow = Color(-33)
	// BrightBlue color (bold Blue)
	BrightBlue = Color(-34)
	// BrightPurple color (bold Purple)
	BrightPurple = Color(-35)
	// BrightCyan color (bold Cyan)
	BrightCyan = Color(-36)
	// White color (bold Gray)
	White = Color(-37)
)

// Colorize returns a colored string with the given colors.
func Colorize(c Color, b BackgroundColor, text string) string {
	return fmt.Sprintf("%s%s%s%s", ResetColors, c, b, text)
}

// Colorizef returns a colored, formatted string with the given colors.
func Colorizef(c Color, b BackgroundColor, format string, args ...interface{}) string {
	return fmt.Sprintf("%s%s%s"+format, ResetColors, c, b, args)
}

// String implements the stringer interface for color
func (c Color) String() string {
	if c == NoColor {
		return ""
	}

	if int(c) < 0 {
		return "\x1b[1m\x1b[" + strconv.Itoa(int(-c)) + "m\x1b[22m"
	}
	return "\x1b[" + strconv.Itoa(int(c)) + "m"
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"sync"

	"github.com/mholt/archiver"
)

var buildScript, pkgDir, distDir, buildDir, releaseDir string

func init() {
	pkgDir = filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "mholt", "caddy")
	buildScript = filepath.Join(pkgDir, "build.bash")
	distDir = filepath.Join(pkgDir, "dist")
	buildDir = filepath.Join(distDir, "builds")
	releaseDir = filepath.Join(distDir, "release")
}

func main() {
	// First, clean up
	err := os.RemoveAll(buildDir)
	if err != nil {
		log.Fatal(err)
	}
	err = os.RemoveAll(releaseDir)
	if err != nil {
		log.Fatal(err)
	}

	// Then set up
	err = os.MkdirAll(buildDir, 0755)
	if err != nil {
		log.Fatal(err)
	}
	err = os.MkdirAll(releaseDir, 0755)
	if err != nil {
		log.Fatal(err)
	}

	// Perform builds and make archives in parallel; only as many
	// goroutines as we have processors.
	var wg sync.WaitGroup
	var throttle = make(chan struct{}, numProcs())
	for _, p := range platforms {
		wg.Add(1)
		throttle <- struct{}{}

		if p.os == "" || p.arch == "" || p.archive == "" {
			log.Fatalf("Platform OS, architecture, and archive format is required: %+v", p)
		}

		go func(p platform) {
			defer wg.Done()
			defer func() { <-throttle }()

			fmt.Printf("== Building %s\n", p)

			var baseFilename, binFilename string
			baseFilename = fmt.Sprintf("caddy_%s_%s", p.os, p.arch)
			if p.arch == "arm" {
				baseFilename += p.arm
			}
			binFilename = baseFilename
			if p.os == "windows" {
				binFilename += ".exe"
			}

			binPath := filepath.Join(buildDir, binFilename)
			archive := filepath.Join(releaseDir, fmt.Sprintf("%s.%s", baseFilename, p.archive))
			archiveContents := append(distContents, binPath)

			err := build(p, binPath)
			if err != nil {
				log.Fatal(err)
			}

			fmt.Printf("== Compressing %s\n", baseFilename)

			if p.archive == "zip" {
				err := archiver.Zip(archive, archiveContents)
				if err != nil {
					log.Fatal(err)
				}
			} else if p.archive == "tar.gz" {
				err := archiver.TarGz(archive, archiveContents)
				if err != nil {
					log.Fatal(err)
				}
			}
		}(p)
	}

	wg.Wait()
}

func build(p platform, out string) error {
	cmd := exec.Command(buildScript, out)
	cmd.Dir = pkgDir
	cmd.Env = os.Environ()
	cmd.Env = append(cmd.Env, "CGO_ENABLED=0")
	cmd.Env = append(cmd.Env, "GOOS="+p.os)
	cmd.Env = append(cmd.Env, "GOARCH="+p.arch)
	cmd.Env = append(cmd.Env, "GOARM="+p.arm)
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

type platform struct {
	os, arch, arm, binExt, archive string
}

func (p platform) String() string {
	outStr := fmt.Sprintf("%s/%s", p.os, p.arch)
	if p.arch == "arm" {
		outStr += fmt.Sprintf(" (ARM v%s)", p.arm)
	}
	return outStr
}

func numProcs() int {
	n := runtime.GOMAXPROCS(0)
	if n == runtime.NumCPU() && n > 1 {
		n -= 1
	}
	return n
}

// See: https://golang.org/doc/install/source#environment
// Not all supported platforms are listed since some are
// problematic and we only build the most common ones.
// These are just the pre-made, readily-available static
// builds, and we can add more upon request if there is
// enough demand.
var platforms = []platform{
	{os: "darwin", arch: "386", archive: "zip"},
	{os: "darwin", arch: "amd64", archive: "zip"},
	{os: "freebsd", arch: "386", archive: "tar.gz"},
	{os: "freebsd", arch: "amd64", archive: "tar.gz"},
	{os: "freebsd", arch: "arm", arm: "7", archive: "tar.gz"},
	{os: "linux", arch: "386", archive: "tar.gz"},
	{os: "linux", arch: "amd64", archive: "tar.gz"},
	{os: "linux", arch: "arm", arm: "7", archive: "tar.gz"},
	{os: "linux", arch: "arm64", archive: "tar.gz"},
	{os: "netbsd", arch: "386", archive: "tar.gz"},
	{os: "netbsd", arch: "amd64", archive: "tar.gz"},
	{os: "openbsd", arch: "386", archive: "tar.gz"},
	{os: "openbsd", arch: "amd64", archive: "tar.gz"},
	{os: "solaris", arch: "amd64", archive: "tar.gz"},
	{os: "windows", arch: "386", archive: "zip"},
	{os: "windows", arch: "amd64", archive: "zip"},
}

var distContents = []string{
	filepath.Join(distDir, "init"),
	filepath.Join(distDir, "CHANGES.txt"),
	filepath.Join(distDir, "LICENSES.txt"),
	filepath.Join(distDir, "README.txt"),
}
<commit_msg>Who uses 32-bit Mac anyway. :P<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"sync"

	"github.com/mholt/archiver"
)

var buildScript, pkgDir, distDir, buildDir, releaseDir string

func init() {
	pkgDir = filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "mholt", "caddy")
	buildScript = filepath.Join(pkgDir, "build.bash")
	distDir = filepath.Join(pkgDir, "dist")
	buildDir = filepath.Join(distDir, "builds")
	releaseDir = filepath.Join(distDir, "release")
}

func main() {
	// First, clean up
	err := os.RemoveAll(buildDir)
	if err != nil {
		log.Fatal(err)
	}
	err = os.RemoveAll(releaseDir)
	if err != nil {
		log.Fatal(err)
	}

	// Then set up
	err = os.MkdirAll(buildDir, 0755)
	if err != nil {
		log.Fatal(err)
	}
	err = os.MkdirAll(releaseDir, 0755)
	if err != nil {
		log.Fatal(err)
	}

	// Perform builds and make archives in parallel; only as many
	// goroutines as we have processors.
	var wg sync.WaitGroup
	var throttle = make(chan struct{}, numProcs())
	for _, p := range platforms {
		wg.Add(1)
		throttle <- struct{}{}

		if p.os == "" || p.arch == "" || p.archive == "" {
			log.Fatalf("Platform OS, architecture, and archive format is required: %+v", p)
		}

		go func(p platform) {
			defer wg.Done()
			defer func() { <-throttle }()

			fmt.Printf("== Building %s\n", p)

			var baseFilename, binFilename string
			baseFilename = fmt.Sprintf("caddy_%s_%s", p.os, p.arch)
			if p.arch == "arm" {
				baseFilename += p.arm
			}
			binFilename = baseFilename
			if p.os == "windows" {
				binFilename += ".exe"
			}

			binPath := filepath.Join(buildDir, binFilename)
			archive := filepath.Join(releaseDir, fmt.Sprintf("%s.%s", baseFilename, p.archive))
			archiveContents := append(distContents, binPath)

			err := build(p, binPath)
			if err != nil {
				log.Fatal(err)
			}

			fmt.Printf("== Compressing %s\n", baseFilename)

			if p.archive == "zip" {
				err := archiver.Zip(archive, archiveContents)
				if err != nil {
					log.Fatal(err)
				}
			} else if p.archive == "tar.gz" {
				err := archiver.TarGz(archive, archiveContents)
				if err != nil {
					log.Fatal(err)
				}
			}
		}(p)
	}

	wg.Wait()
}

func build(p platform, out string) error {
	cmd := exec.Command(buildScript, out)
	cmd.Dir = pkgDir
	cmd.Env = os.Environ()
	cmd.Env = append(cmd.Env, "CGO_ENABLED=0")
	cmd.Env = append(cmd.Env, "GOOS="+p.os)
	cmd.Env = append(cmd.Env, "GOARCH="+p.arch)
	cmd.Env = append(cmd.Env, "GOARM="+p.arm)
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

type platform struct {
	os, arch, arm, binExt, archive string
}

func (p platform) String() string {
	outStr := fmt.Sprintf("%s/%s", p.os, p.arch)
	if p.arch == "arm" {
		outStr += fmt.Sprintf(" (ARM v%s)", p.arm)
	}
	return outStr
}

func numProcs() int {
	n := runtime.GOMAXPROCS(0)
	if n == runtime.NumCPU() && n > 1 {
		n -= 1
	}
	return n
}

// See: https://golang.org/doc/install/source#environment
// Not all supported platforms are listed since some are
// problematic and we only build the most common ones.
// These are just the pre-made, readily-available static
// builds, and we can add more upon request if there is
// enough demand.
var platforms = []platform{
	{os: "darwin", arch: "amd64", archive: "zip"},
	{os: "freebsd", arch: "386", archive: "tar.gz"},
	{os: "freebsd", arch: "amd64", archive: "tar.gz"},
	{os: "freebsd", arch: "arm", arm: "7", archive: "tar.gz"},
	{os: "linux", arch: "386", archive: "tar.gz"},
	{os: "linux", arch: "amd64", archive: "tar.gz"},
	{os: "linux", arch: "arm", arm: "7", archive: "tar.gz"},
	{os: "linux", arch: "arm64", archive: "tar.gz"},
	{os: "netbsd", arch: "386", archive: "tar.gz"},
	{os: "netbsd", arch: "amd64", archive: "tar.gz"},
	{os: "openbsd", arch: "386", archive: "tar.gz"},
	{os: "openbsd", arch: "amd64", archive: "tar.gz"},
	{os: "solaris", arch: "amd64", archive: "tar.gz"},
	{os: "windows", arch: "386", archive: "zip"},
	{os: "windows", arch: "amd64", archive: "zip"},
}

var distContents = []string{
	filepath.Join(distDir, "init"),
	filepath.Join(distDir, "CHANGES.txt"),
	filepath.Join(distDir, "LICENSES.txt"),
	filepath.Join(distDir, "README.txt"),
}
<|endoftext|>


<commit_before>package timeseries

import (
	"errors"
	"time"
)

// Explanation
// Have several granularity buckets
// 1s, 1m, 5m, ...
// The buckets will be in circular arrays
//
// For example we could have
// 60 1s buckets to make up 1 minute
// 60 1m buckets to make up 1 hour
// ...
// This would enable us to get the last 1 minute data at 1s granularity (every second)
//
// Date ranges are [start, end[
//
// Put:
// Every time an event comes we add it to all corresponding buckets
//
// Example:
// Event time = 12:00:00
// 1s bucket = 12:00:00
// 1m bucket = 12:00:00
// 5m bucket = 12:00:00
//
// Event time = 12:00:01
// 1s bucket = 12:00:01
// 1m bucket = 12:00:00
// 5m bucket = 12:00:00
//
// Event time = 12:01:01
// 1s bucket = 12:01:01
// 1m bucket = 12:01:00
// 5m bucket = 12:00:00
//
// Fetch:
// Given a time span we try to find the buckets with the finest granularity
// to satisfy the time span and return the sum of their contents
//
// Example:
// Now = 12:05:30
// Time span = 12:05:00 - 12:05:02
// Return sum of 1s buckets 0,1
//
// Now = 12:10:00
// Time span = 12:05:00 - 12:07:00
// Return sum of 1m buckets 5,6
//
// Now = 12:10:00
// Time span = 12:00:00 - 12:10:00 (last 10 minutes)
// Return sum of 5m buckets 0,1
//
// Now = 12:10:01
// Time span = 12:05:01 - 12:10:01 (last 5 minutes)
// Return sum of 5m buckets (59/(5*60))*1, (1/(5*60))*2
//
// Now = 12:10:01
// Time span = 12:04:01 - 12:10:01 (last 6 minutes)
// Return sum of 1m buckets (59/60)*4, 5, 6, 7, 8, 9, (1/60)*10

var (
	ErrBadRange         = errors.New("range is invalid")
	ErrBadGranularities = errors.New("granularities must be strictly increasing and non empty")
	ErrRangeNotCovered  = errors.New("range is not convered")
)

var defaultGranularities = []time.Duration{
	time.Second,
	time.Minute,
	time.Hour,
}

type Clock interface {
	Now() time.Time
}

type defaultClock struct{}

func (c *defaultClock) Now() time.Time {
	return time.Now()
}

type TimeSeries interface {
	Increase(amount int)
	IncreaseAtTime(amount int, insertTime time.Time)
	Recent(duration time.Duration) (float64, error)
	Range(start, end time.Time) (float64, error)
}

type timeseries struct {
	clock       Clock
	levels      []level
	pending     int
	pendingTime time.Time
}

// NewTimeseries creates a new TimeSeries with default granularities
func NewTimeseries() (TimeSeries, error) {
	return NewTimeseriesWithGranularities(defaultGranularities)
}

// NewTimeseriesWithGranularities creates a new TimeSeries with provided granularities
// ErrBadGranularities is returned if granularities are not in increasing order.
func NewTimeseriesWithGranularities(granularities []time.Duration) (TimeSeries, error) {
	err := checkGranularities(granularities)
	if err != nil {
		return nil, err
	}
	clock := &defaultClock{}
	return &timeseries{clock: clock, levels: createLevels(clock, granularities)}, nil
}

func checkGranularities(granularities []time.Duration) error {
	if len(granularities) == 0 {
		return ErrBadGranularities
	}
	last := granularities[0]
	for i := 1; i < len(granularities); i++ {
		if granularities[i] <= last {
			return ErrBadGranularities
		}
		last = granularities[i]
	}
	return nil
}

func createLevels(clock Clock, granularities []time.Duration) []level {
	levels := make([]level, len(granularities))
	for i := range granularities {
		levels[i] = newLevel(clock, granularities[i], 60)
	}
	return levels
}

// Increase adds amount at current time
func (t *timeseries) Increase(amount int) {
	t.IncreaseAtTime(amount, t.clock.Now())
}

// IncreaseAtTime adds amount at a specific time
func (t *timeseries) IncreaseAtTime(amount int, time time.Time) {
	if time.After(t.pendingTime) {
		t.advance(time)
		t.handlePending()
		t.pendingTime = t.levels[0].latest()
		t.pending = amount
	} else if time.After(t.pendingTime.Add(-t.levels[0].granularity)) {
		t.pending++
	} else {
		t.increaseAtTime(amount, time)
	}
}

func (t *timeseries) increaseAtTime(amount int, time time.Time) {
	for i := range t.levels {
		if time.Before(t.levels[i].latest().Add(-1 * t.levels[i].duration())) {
			continue
		}
		t.levels[i].increaseAtTime(amount, time)
	}
}

func (t *timeseries) handlePending() {
	t.increaseAtTime(t.pending, t.pendingTime)
	t.pending = 0
}

func (t *timeseries) advance(target time.Time) {
	for i := range t.levels {
		if !target.Before(t.levels[i].latest().Add(t.levels[i].duration())) {
			t.levels[i].clear(target)
			continue
		}
		t.levels[i].advance(target)
	}
}

// Recent returns the sum over [now-duration, now)
func (t *timeseries) Recent(duration time.Duration) (float64, error) {
	// TODO: advance to now
	now := t.clock.Now()
	return t.Range(now.Add(-duration), now)
}

// Range returns the sum over the given range [start, end)
// ErrBadRange is returned if start is after end.
// ErrRangeNotCovered is returned if the range lies outside the time series.
func (t *timeseries) Range(start, end time.Time) (float64, error) {
	if start.After(end) {
		return 0, ErrBadRange
	}
	if ok, err := t.intersects(start, end); !ok {
		return 0, err
	}
	//start, end = t.clamp(start, end)
	for i := range t.levels {
		// use !start.Before so earliest() is included
		// if we use earliest().Before() we won't get start
		if !start.Before(t.levels[i].earliest()) {
			return t.levels[i].sumInterval(start, end), nil
		}
	}
	return 0, nil
}

func (t *timeseries) intersects(start, end time.Time) (bool, error) {
	biggestLevel := t.levels[len(t.levels)-1]
	if end.Before(biggestLevel.latest().Add(-biggestLevel.duration())) {
		return false, ErrRangeNotCovered
	}
	if start.After(t.levels[0].latest()) {
		return false, ErrRangeNotCovered
	}
	return true, nil
}
<commit_msg>remove dead code<commit_after>package timeseries

import (
	"errors"
	"time"
)

// Explanation
// Have several granularity buckets
// 1s, 1m, 5m, ...
// The buckets will be in circular arrays
//
// For example we could have
// 60 1s buckets to make up 1 minute
// 60 1m buckets to make up 1 hour
// ...
// This would enable us to get the last 1 minute data at 1s granularity (every second)
//
// Date ranges are [start, end[
//
// Put:
// Every time an event comes we add it to all corresponding buckets
//
// Example:
// Event time = 12:00:00
// 1s bucket = 12:00:00
// 1m bucket = 12:00:00
// 5m bucket = 12:00:00
//
// Event time = 12:00:01
// 1s bucket = 12:00:01
// 1m bucket = 12:00:00
// 5m bucket = 12:00:00
//
// Event time = 12:01:01
// 1s bucket = 12:01:01
// 1m bucket = 12:01:00
// 5m bucket = 12:00:00
//
// Fetch:
// Given a time span we try to find the buckets with the finest granularity
// to satisfy the time span and return the sum of their contents
//
// Example:
// Now = 12:05:30
// Time span = 12:05:00 - 12:05:02
// Return sum of 1s buckets 0,1
//
// Now = 12:10:00
// Time span = 12:05:00 - 12:07:00
// Return sum of 1m buckets 5,6
//
// Now = 12:10:00
// Time span = 12:00:00 - 12:10:00 (last 10 minutes)
// Return sum of 5m buckets 0,1
//
// Now = 12:10:01
// Time span = 12:05:01 - 12:10:01 (last 5 minutes)
// Return sum of 5m buckets (59/(5*60))*1, (1/(5*60))*2
//
// Now = 12:10:01
// Time span = 12:04:01 - 12:10:01 (last 6 minutes)
// Return sum of 1m buckets (59/60)*4, 5, 6, 7, 8, 9, (1/60)*10

var (
	ErrBadRange         = errors.New("range is invalid")
	ErrBadGranularities = errors.New("granularities must be strictly increasing and non empty")
	ErrRangeNotCovered  = errors.New("range is not convered")
)

var defaultGranularities = []time.Duration{
	time.Second,
	time.Minute,
	time.Hour,
}

type Clock interface {
	Now() time.Time
}

type defaultClock struct{}

func (c *defaultClock) Now() time.Time {
	return time.Now()
}

type TimeSeries interface {
	Increase(amount int)
	IncreaseAtTime(amount int, insertTime time.Time)
	Recent(duration time.Duration) (float64, error)
	Range(start, end time.Time) (float64, error)
}

type timeseries struct {
	clock       Clock
	levels      []level
	pending     int
	pendingTime time.Time
}

// NewTimeseries creates a new TimeSeries with default granularities
func NewTimeseries() (TimeSeries, error) {
	return NewTimeseriesWithGranularities(defaultGranularities)
}

// NewTimeseriesWithGranularities creates a new TimeSeries with provided granularities
// ErrBadGranularities is returned if granularities are not in increasing order.
func NewTimeseriesWithGranularities(granularities []time.Duration) (TimeSeries, error) {
	err := checkGranularities(granularities)
	if err != nil {
		return nil, err
	}
	clock := &defaultClock{}
	return &timeseries{clock: clock, levels: createLevels(clock, granularities)}, nil
}

func checkGranularities(granularities []time.Duration) error {
	if len(granularities) == 0 {
		return ErrBadGranularities
	}
	last := granularities[0]
	for i := 1; i < len(granularities); i++ {
		if granularities[i] <= last {
			return ErrBadGranularities
		}
		last = granularities[i]
	}
	return nil
}

func createLevels(clock Clock, granularities []time.Duration) []level {
	levels := make([]level, len(granularities))
	for i := range granularities {
		levels[i] = newLevel(clock, granularities[i], 60)
	}
	return levels
}

// Increase adds amount at current time
func (t *timeseries) Increase(amount int) {
	t.IncreaseAtTime(amount, t.clock.Now())
}

// IncreaseAtTime adds amount at a specific time
func (t *timeseries) IncreaseAtTime(amount int, time time.Time) {
	if time.After(t.pendingTime) {
		t.advance(time)
		t.handlePending()
		t.pendingTime = t.levels[0].latest()
		t.pending = amount
	} else if time.After(t.pendingTime.Add(-t.levels[0].granularity)) {
		t.pending++
	} else {
		t.increaseAtTime(amount, time)
	}
}

func (t *timeseries) increaseAtTime(amount int, time time.Time) {
	for i := range t.levels {
		if time.Before(t.levels[i].latest().Add(-1 * t.levels[i].duration())) {
			continue
		}
		t.levels[i].increaseAtTime(amount, time)
	}
}

func (t *timeseries) handlePending() {
	t.increaseAtTime(t.pending, t.pendingTime)
	t.pending = 0
}

func (t *timeseries) advance(target time.Time) {
	for i := range t.levels {
		if !target.Before(t.levels[i].latest().Add(t.levels[i].duration())) {
			t.levels[i].clear(target)
			continue
		}
		t.levels[i].advance(target)
	}
}

// Recent returns the sum over [now-duration, now)
func (t *timeseries) Recent(duration time.Duration) (float64, error) {
	// TODO: advance to now
	now := t.clock.Now()
	return t.Range(now.Add(-duration), now)
}

// Range returns the sum over the given range [start, end)
// ErrBadRange is returned if start is after end.
// ErrRangeNotCovered is returned if the range lies outside the time series.
func (t *timeseries) Range(start, end time.Time) (float64, error) {
	if start.After(end) {
		return 0, ErrBadRange
	}
	if ok, err := t.intersects(start, end); !ok {
		return 0, err
	}
	for i := range t.levels {
		// use !start.Before so earliest() is included
		// if we use earliest().Before() we won't get start
		if !start.Before(t.levels[i].earliest()) {
			return t.levels[i].sumInterval(start, end), nil
		}
	}
	return 0, nil
}

func (t *timeseries) intersects(start, end time.Time) (bool, error) {
	biggestLevel := t.levels[len(t.levels)-1]
	if end.Before(biggestLevel.latest().Add(-biggestLevel.duration())) {
		return false, ErrRangeNotCovered
	}
	if start.After(t.levels[0].latest()) {
		return false, ErrRangeNotCovered
	}
	return true, nil
}
<|endoftext|>


<commit_before>package discrete_test

import (
	"github.com/gonum/discrete"
	"testing"
)

func TestTileGraph(t *testing.T) {
	tg := discrete.NewTileGraph(4, 4, false)
	if tg == nil || tg.String() != "▀▀▀▀\n▀▀▀▀\n▀▀▀▀\n▀▀▀▀" {
		t.Fatal("Tile graph not generated correctly")
	}

	tg.SetPassability(0, 1, true)
	if tg == nil || tg.String() != "▀ ▀▀\n▀▀▀▀\n▀▀▀▀\n▀▀▀▀" {
		t.Fatal("Passability set incorrectly")
	}

	tg.SetPassability(0, 1, false)
	if tg == nil || tg.String() != "▀▀▀▀\n▀▀▀▀\n▀▀▀▀\n▀▀▀▀" {
		t.Fatal("Passability set incorrectly")
	}

	tg.SetPassability(0, 1, true)
	if tg == nil || tg.String() != "▀ ▀▀\n▀▀▀▀\n▀▀▀▀\n▀▀▀▀" {
		t.Fatal("Passability set incorrectly")
	}

	tg.SetPassability(0, 2, true)
	if tg == nil || tg.String() != "▀  ▀\n▀▀▀▀\n▀▀▀▀\n▀▀▀▀" {
		t.Fatal("Passability set incorrectly")
	}

	tg.SetPassability(1, 2, true)
	if tg == nil || tg.String() != "▀  ▀\n▀▀ ▀\n▀▀▀▀\n▀▀▀▀" {
		t.Fatal("Passability set incorrectly")
	}

	tg.SetPassability(2, 2, true)
	if tg == nil || tg.String() != "▀  ▀\n▀▀ ▀\n▀▀ ▀\n▀▀▀▀" {
		t.Fatal("Passability set incorrectly")
	}

	tg.SetPassability(3, 2, true)
	if tg == nil || tg.String() != "▀  ▀\n▀▀ ▀\n▀▀ ▀\n▀▀ ▀" {
		t.Fatal("Passability set incorrectly")
	}

	if tg2, err := discrete.GenerateTileGraph("▀  ▀\n▀▀ ▀\n▀▀ ▀\n▀▀ ▀"); err != nil {
		t.Error("Tile graph errored on interpreting valid template string\n▀  ▀\n▀▀ ▀\n▀▀ ▀\n▀▀ ▀")
	} else if tg2.String() != "▀  ▀\n▀▀ ▀\n▀▀ ▀\n▀▀ ▀" {
		t.Error("Tile graph failed to generate properly with input string\n▀  ▀\n▀▀ ▀\n▀▀ ▀\n▀▀ ▀")
	}

	if tg.CoordsToID(0, 0) != 0 {
		t.Error("Coords to ID fails on 0,0")
	} else if tg.CoordsToID(3, 3) != 15 {
		t.Error("Coords to ID fails on 3,3")
	} else if tg.CoordsToID(0, 3) != 3 {
		t.Error("Coords to ID fails on 0,3")
	} else if tg.CoordsToID(3, 0) != 12 {
		t.Error("Coords to ID fails on 3,0")
	}

	if r, c := tg.IDToCoords(0); r != 0 || c != 0 {
		t.Error("ID to Coords fails on 0,0")
	} else if r, c := tg.IDToCoords(15); r != 3 || c != 3 {
		t.Error("ID to Coords fails on 3,3")
	} else if r, c := tg.IDToCoords(3); r != 0 || c != 3 {
		t.Error("ID to Coords fails on 0,3")
	} else if r, c := tg.IDToCoords(12); r != 3 || c != 0 {
		t.Error("ID to Coords fails on 3,0")
	}

	if succ := tg.Successors(0); succ != nil || len(succ) != 0 {
		t.Error("Successors for impassable tile not 0")
	}

	if succ := tg.Successors(2); succ == nil || len(succ) != 2 {
		t.Error("Incorrect number of successors for (0,2)")
	} else {
		for _, s := range succ {
			if s != 1 && s != 6 {
				t.Error("Successor for (0,2) neither (0,1) nor (1,2)")
			}
		}
	}

	if tg.Degree(2) != 4 {
		t.Error("Degree returns incorrect number for (0,2)")
	}
	if tg.Degree(1) != 2 {
		t.Error("Degree returns incorrect number for (0,2)")
	}
	if tg.Degree(0) != 0 {
		t.Error("Degree returns incorrect number for impassable tile (0,0)")
	}

}

func TestSimpleAStar(t *testing.T) {
	tg, err := discrete.GenerateTileGraph("▀  ▀\n▀▀ ▀\n▀▀ ▀\n▀▀ ▀")
	if err != nil {
		t.Fatal("Couldn't generate tilegraph")
	}

	path, cost, _ := discrete.AStar(1, 14, tg, nil, nil)
	if cost != 4.0 {
		t.Errorf("A* reports incorrect cost for simple tilegraph search")
	}

	if path == nil {
		t.Fatalf("A* fails to find path for for simple tilegraph search")
	} else {
		correctPath := []int{1, 2, 6, 10, 14}
		if len(path) != len(correctPath) {
			t.Fatalf("Astar returns wrong length path for simple tilegraph search")
		}
		for i, node := range path {
			if node != correctPath[i] {
				t.Errorf("Astar returns wrong path at step", i, "got:", node, "actual:", correctPath[i])
			}
		}
	}
}

func TestHarderAStar(t *testing.T) {
	tg := discrete.NewTileGraph(3, 3, true)

	path, cost, _ := discrete.AStar(0, 8, tg, nil, nil)

	if cost != 4.0 || !discrete.IsPath(path, tg) {
		t.Error("Non-optimal or impossible path found")
	}
}
<commit_msg>One more A* test<commit_after>package discrete_test

import (
	"github.com/gonum/discrete"
	"testing"
)

func TestTileGraph(t *testing.T) {
	tg := discrete.NewTileGraph(4, 4, false)
	if tg == nil || tg.String() != "▀▀▀▀\n▀▀▀▀\n▀▀▀▀\n▀▀▀▀" {
		t.Fatal("Tile graph not generated correctly")
	}

	tg.SetPassability(0, 1, true)
	if tg == nil || tg.String() != "▀ ▀▀\n▀▀▀▀\n▀▀▀▀\n▀▀▀▀" {
		t.Fatal("Passability set incorrectly")
	}

	tg.SetPassability(0, 1, false)
	if tg == nil || tg.String() != "▀▀▀▀\n▀▀▀▀\n▀▀▀▀\n▀▀▀▀" {
		t.Fatal("Passability set incorrectly")
	}

	tg.SetPassability(0, 1, true)
	if tg == nil || tg.String() != "▀ ▀▀\n▀▀▀▀\n▀▀▀▀\n▀▀▀▀" {
		t.Fatal("Passability set incorrectly")
	}

	tg.SetPassability(0, 2, true)
	if tg == nil || tg.String() != "▀  ▀\n▀▀▀▀\n▀▀▀▀\n▀▀▀▀" {
		t.Fatal("Passability set incorrectly")
	}

	tg.SetPassability(1, 2, true)
	if tg == nil || tg.String() != "▀  ▀\n▀▀ ▀\n▀▀▀▀\n▀▀▀▀" {
		t.Fatal("Passability set incorrectly")
	}

	tg.SetPassability(2, 2, true)
	if tg == nil || tg.String() != "▀  ▀\n▀▀ ▀\n▀▀ ▀\n▀▀▀▀" {
		t.Fatal("Passability set incorrectly")
	}

	tg.SetPassability(3, 2, true)
	if tg == nil || tg.String() != "▀  ▀\n▀▀ ▀\n▀▀ ▀\n▀▀ ▀" {
		t.Fatal("Passability set incorrectly")
	}

	if tg2, err := discrete.GenerateTileGraph("▀  ▀\n▀▀ ▀\n▀▀ ▀\n▀▀ ▀"); err != nil {
		t.Error("Tile graph errored on interpreting valid template string\n▀  ▀\n▀▀ ▀\n▀▀ ▀\n▀▀ ▀")
	} else if tg2.String() != "▀  ▀\n▀▀ ▀\n▀▀ ▀\n▀▀ ▀" {
		t.Error("Tile graph failed to generate properly with input string\n▀  ▀\n▀▀ ▀\n▀▀ ▀\n▀▀ ▀")
	}

	if tg.CoordsToID(0, 0) != 0 {
		t.Error("Coords to ID fails on 0,0")
	} else if tg.CoordsToID(3, 3) != 15 {
		t.Error("Coords to ID fails on 3,3")
	} else if tg.CoordsToID(0, 3) != 3 {
		t.Error("Coords to ID fails on 0,3")
	} else if tg.CoordsToID(3, 0) != 12 {
		t.Error("Coords to ID fails on 3,0")
	}

	if r, c := tg.IDToCoords(0); r != 0 || c != 0 {
		t.Error("ID to Coords fails on 0,0")
	} else if r, c := tg.IDToCoords(15); r != 3 || c != 3 {
		t.Error("ID to Coords fails on 3,3")
	} else if r, c := tg.IDToCoords(3); r != 0 || c != 3 {
		t.Error("ID to Coords fails on 0,3")
	} else if r, c := tg.IDToCoords(12); r != 3 || c != 0 {
		t.Error("ID to Coords fails on 3,0")
	}

	if succ := tg.Successors(0); succ != nil || len(succ) != 0 {
		t.Error("Successors for impassable tile not 0")
	}

	if succ := tg.Successors(2); succ == nil || len(succ) != 2 {
		t.Error("Incorrect number of successors for (0,2)")
	} else {
		for _, s := range succ {
			if s != 1 && s != 6 {
				t.Error("Successor for (0,2) neither (0,1) nor (1,2)")
			}
		}
	}

	if tg.Degree(2) != 4 {
		t.Error("Degree returns incorrect number for (0,2)")
	}
	if tg.Degree(1) != 2 {
		t.Error("Degree returns incorrect number for (0,2)")
	}
	if tg.Degree(0) != 0 {
		t.Error("Degree returns incorrect number for impassable tile (0,0)")
	}

}

func TestSimpleAStar(t *testing.T) {
	tg, err := discrete.GenerateTileGraph("▀  ▀\n▀▀ ▀\n▀▀ ▀\n▀▀ ▀")
	if err != nil {
		t.Fatal("Couldn't generate tilegraph")
	}

	path, cost, _ := discrete.AStar(1, 14, tg, nil, nil)
	if cost != 4.0 {
		t.Errorf("A* reports incorrect cost for simple tilegraph search")
	}

	if path == nil {
		t.Fatalf("A* fails to find path for for simple tilegraph search")
	} else {
		correctPath := []int{1, 2, 6, 10, 14}
		if len(path) != len(correctPath) {
			t.Fatalf("Astar returns wrong length path for simple tilegraph search")
		}
		for i, node := range path {
			if node != correctPath[i] {
				t.Errorf("Astar returns wrong path at step", i, "got:", node, "actual:", correctPath[i])
			}
		}
	}
}

func TestHarderAStar(t *testing.T) {
	tg := discrete.NewTileGraph(3, 3, true)

	path, cost, _ := discrete.AStar(0, 8, tg, nil, nil)

	if cost != 4.0 || !discrete.IsPath(path, tg) {
		t.Error("Non-optimal or impossible path found for 3x3 grid")
	}

	tg = discrete.NewTileGraph(1000, 1000, true)
	path, cost, _ = discrete.AStar(00, 999*1000+999, tg, nil, nil)
	if !discrete.IsPath(path, tg) || cost != 1998.0 {
		t.Error("Non-optimal or impossible path found for 100x100 grid; cost:", cost, "path:\n"+tg.PathString(path))
	}
}
<|endoftext|>


<commit_before>package tnetstring

import (
	"os"
	"reflect"
	"strconv"
)

type outbuf struct {
	buf []byte
	// n is the index we last wrote to or the amount of space left,
	// depending on how you look at it
	n int
}

func Marshal(v interface{}) (string, os.Error) {
	val := reflect.ValueOf(v)
	b := new(outbuf)
	b.n = 20
	b.buf = make([]byte, b.n)
	if err := marshal(b, val); err != nil {
		return "", err
	}
	return string(b.buf[b.n:]), nil
}

var typeLookup = [...]byte{
	reflect.Invalid: '~',
	reflect.Bool:    '!',
	reflect.Int:     '#',
	reflect.Int8:    '#',
	reflect.Int16:   '#',
	reflect.Int32:   '#',
	reflect.Int64:   '#',
	reflect.Uint:    '#',
	reflect.Uint8:   '#',
	reflect.Uint16:  '#',
	reflect.Uint32:  '#',
	reflect.Uint64:  '#',
	reflect.Uintptr: '#',
	reflect.String:  ',',
	reflect.Array:   ']',
	reflect.Slice:   ']',
	reflect.Map:     '}',
	reflect.Struct:  '}',
	// include last item so the array has the right length
	reflect.UnsafePointer: 0,
}

func marshal(b *outbuf, v reflect.Value) os.Error {
	v = indirect(v, false)
	kind := v.Kind()
	typ := typeLookup[kind]
	if typ == 0 {
		return os.NewError("tnetstring: unsupported type")
	}
	b.writeByte(typ)
	orig := len(b.buf) - b.n
	switch kind {
	case reflect.Invalid:
	case reflect.Bool:
		str := "false"
		if v.Bool() {
			str = "true"
		}
		b.writeString(str)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		str := strconv.Itoa64(v.Int())
		b.writeString(str)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
		reflect.Uint64, reflect.Uintptr:
		str := strconv.Uitoa64(v.Uint())
		b.writeString(str)
	case reflect.String:
		b.writeString(v.String())
	case reflect.Array, reflect.Slice:
		l := v.Len()
		for i := l - 1; i >= 0; i-- {
			err := marshal(b, v.Index(i))
			if err != nil {
				return err
			}
		}
	case reflect.Map:
		if v.Type().Key().Kind() != reflect.String {
			return os.NewError("tnetstring: only maps with string keys can be marshaled")
		}
		keys := v.MapKeys()
		for _, key := range keys {
			if err := marshal(b, v.MapIndex(key)); err != nil {
				return err
			}
			b.writeByte(',')
			orig := len(b.buf) - b.n
			b.writeString(key.String())
			b.writeLen(orig)
		}
	case reflect.Struct:
		t := v.Type()
		l := t.NumField()
		for i := l - 1; i >= 0; i-- {
			field := t.Field(i)
			str := field.Tag
			if str == "" {
				str = field.Name
			}
			if err := marshal(b, v.Field(i)); err != nil {
				return err
			}
			b.writeByte(',')
			orig := len(b.buf) - b.n
			b.writeString(str)
			b.writeLen(orig)
		}
	default:
		panic("unreachable")
	}
	b.writeLen(orig)
	return nil
}

func (buf *outbuf) writeByte(b byte) {
	if buf.n <= 0 {
		buf.grow(1)
	}
	buf.n--
	buf.buf[buf.n] = b
}

func (b *outbuf) writeString(s string) {
	if b.n < len(s) {
		b.grow(len(s))
	}
	b.n -= len(s)
	copy(b.buf[b.n:], s)
}

func (b *outbuf) writeLen(orig int) {
	l := len(b.buf) - b.n - orig
	str := strconv.Itoa(l)
	b.writeByte(':')
	b.writeString(str)
}

func (b *outbuf) grow(n int) {
	l := len(b.buf)
	need := 2 * l
	if need < l+n-b.n {
		need = l + n - b.n
	}
	buf := make([]byte, need)
	copy(buf[need-l+b.n:], b.buf[b.n:])
	b.buf = buf
	b.n = need - l + b.n
}
<commit_msg>Switch on type instead of kind when marshaling<commit_after>package tnetstring

import (
	"os"
	"reflect"
	"strconv"
)

type outbuf struct {
	buf []byte
	// n is the index we last wrote to or the amount of space left,
	// depending on how you look at it
	n int
}

func Marshal(v interface{}) (string, os.Error) {
	val := reflect.ValueOf(v)
	b := new(outbuf)
	b.n = 20
	b.buf = make([]byte, b.n)
	if err := marshal(b, val); err != nil {
		return "", err
	}
	return string(b.buf[b.n:]), nil
}

var typeLookup = [...]byte{
	reflect.Invalid: '~',
	reflect.Bool:    '!',
	reflect.Int:     '#',
	reflect.Int8:    '#',
	reflect.Int16:   '#',
	reflect.Int32:   '#',
	reflect.Int64:   '#',
	reflect.Uint:    '#',
	reflect.Uint8:   '#',
	reflect.Uint16:  '#',
	reflect.Uint32:  '#',
	reflect.Uint64:  '#',
	reflect.Uintptr: '#',
	reflect.String:  ',',
	reflect.Array:   ']',
	reflect.Slice:   ']',
	reflect.Map:     '}',
	reflect.Struct:  '}',
	// include last item so the array has the right length
	reflect.UnsafePointer: 0,
}

func marshal(b *outbuf, v reflect.Value) os.Error {
	v = indirect(v, false)
	kind := v.Kind()
	typ := typeLookup[kind]
	if typ == 0 {
		return os.NewError("tnetstring: unsupported type")
	}
	b.writeByte(typ)
	orig := len(b.buf) - b.n
	switch typ {
	case '~':
	case '!':
		str := "false"
		if v.Bool() {
			str = "true"
		}
		b.writeString(str)
	case '#':
		if reflect.Int <= kind && kind <= reflect.Int64 {
			str := strconv.Itoa64(v.Int())
			b.writeString(str)
		} else {
			str := strconv.Uitoa64(v.Uint())
			b.writeString(str)
		}
	case ',':
		b.writeString(v.String())
	case ']':
		l := v.Len()
		for i := l - 1; i >= 0; i-- {
			err := marshal(b, v.Index(i))
			if err != nil {
				return err
			}
		}
	case '}':
		if kind == reflect.Map {
			if v.Type().Key().Kind() != reflect.String {
				return os.NewError("tnetstring: only maps with string keys can be marshaled")
			}
			keys := v.MapKeys()
			for _, key := range keys {
				if err := marshal(b, v.MapIndex(key)); err != nil {
					return err
				}
				b.writeByte(',')
				orig := len(b.buf) - b.n
				b.writeString(key.String())
				b.writeLen(orig)
			}
		} else {
			t := v.Type()
			l := t.NumField()
			for i := l - 1; i >= 0; i-- {
				field := t.Field(i)
				str := field.Tag
				if str == "" {
					str = field.Name
				}
				if err := marshal(b, v.Field(i)); err != nil {
					return err
				}
				b.writeByte(',')
				orig := len(b.buf) - b.n
				b.writeString(str)
				b.writeLen(orig)
			}
		}
	default:
		panic("unreachable")
	}
	b.writeLen(orig)
	return nil
}

func (buf *outbuf) writeByte(b byte) {
	if buf.n <= 0 {
		buf.grow(1)
	}
	buf.n--
	buf.buf[buf.n] = b
}

func (b *outbuf) writeString(s string) {
	if b.n < len(s) {
		b.grow(len(s))
	}
	b.n -= len(s)
	copy(b.buf[b.n:], s)
}

func (b *outbuf) writeLen(orig int) {
	l := len(b.buf) - b.n - orig
	str := strconv.Itoa(l)
	b.writeByte(':')
	b.writeString(str)
}

func (b *outbuf) grow(n int) {
	l := len(b.buf)
	need := 2 * l
	if need < l+n-b.n {
		need = l + n - b.n
	}
	buf := make([]byte, need)
	copy(buf[need-l+b.n:], b.buf[b.n:])
	b.buf = buf
	b.n = need - l + b.n
}
<|endoftext|>


<commit_before>// Copyright 2022 Nokia
// Licensed under the BSD 3-Clause License.
// SPDX-License-Identifier: BSD-3-Clause

package v1alpha1

import (
	"errors"
	"regexp"
	"strings"
)

var (
	engineeringVersions = []string{
		"",
		"latest",
		"ga",
	}
)

// ErrVersionParse is an error which is raised when srlinux version is failed to parse.
var ErrVersionParse = errors.New("version parsing failed")

// SrlVersion represents an sr linux version as a set of fields.
type SrlVersion struct {
	Major  string `json:"major,omitempty"`
	Minor  string `json:"minor,omitempty"`
	Patch  string `json:"patch,omitempty"`
	Build  string `json:"build,omitempty"`
	Commit string `json:"commit,omitempty"`
}

func parseVersionString(s string) (*SrlVersion, error) {
	// Check if the version string is an engineering build with major = 0
	for _, ver := range engineeringVersions {
		if ver == strings.ToLower(s) {
			return &SrlVersion{"0", "", "", "", ""}, nil
		}
	}

	// https://regex101.com/r/eWS6Ms/3
	re := regexp.MustCompile(
		`(?P<major>\d{1,3})\.(?P<minor>\d{1,2})\.?(?P<patch>\d{1,2})?-?(?P<build>\d{1,10})?-?(?P<commit>\S+)?`,
	)

	v := re.FindStringSubmatch(s)
	if v == nil {
		return nil, ErrVersionParse
	}

	return &SrlVersion{v[1], v[2], v[3], v[4], v[5]}, nil
}
<commit_msg>go fmt<commit_after>// Copyright 2022 Nokia
// Licensed under the BSD 3-Clause License.
// SPDX-License-Identifier: BSD-3-Clause

package v1alpha1

import (
	"errors"
	"regexp"
	"strings"
)

// ErrVersionParse is an error which is raised when srlinux version is failed to parse.
var ErrVersionParse = errors.New("version parsing failed")

// SrlVersion represents an sr linux version as a set of fields.
type SrlVersion struct {
	Major  string `json:"major,omitempty"`
	Minor  string `json:"minor,omitempty"`
	Patch  string `json:"patch,omitempty"`
	Build  string `json:"build,omitempty"`
	Commit string `json:"commit,omitempty"`
}

func parseVersionString(s string) (*SrlVersion, error) {
	// Check if the version string is an engineering build with major = 0
	engineeringVersions := []string{"", "latest", "ga"}
	for _, ver := range engineeringVersions {
		if ver == strings.ToLower(s) {
			return &SrlVersion{"0", "", "", "", ""}, nil
		}
	}

	// https://regex101.com/r/eWS6Ms/3
	re := regexp.MustCompile(
		`(?P<major>\d{1,3})\.(?P<minor>\d{1,2})\.?(?P<patch>\d{1,2})?-?(?P<build>\d{1,10})?-?(?P<commit>\S+)?`,
	)

	v := re.FindStringSubmatch(s)
	if v == nil {
		return nil, ErrVersionParse
	}

	return &SrlVersion{v[1], v[2], v[3], v[4], v[5]}, nil
}
<|endoftext|>


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

package sdk

import (
	"bytes"
	"fmt"
	"os"
	"path/filepath"
	"text/template"

	"github.com/coreos/mantle/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
	"github.com/coreos/mantle/system/exec"
	"github.com/coreos/mantle/system/user"
	"github.com/coreos/mantle/util"
)

// Must run inside the SDK chroot, easiest to just assemble a script to do it
const (
	safePath   = "PATH=/usr/sbin:/usr/bin:/sbin:/bin"
	sudoPrompt = "--prompt=sudo password for %p: "
	script     = `#!/bin/bash
set -e

# make sure user/group database files exist
touch /etc/{group,gshadow,passwd,shadow}
chmod 0640 /etc/{gshadow,shadow}

# add group if it doesn't exist already
if ! getent group {{printf "%q" .Groupname}} >/dev/null; then
	echo Adding group {{printf "%q" .Groupname}}
	groupadd -o -g {{.Gid}} {{printf "%q" .Groupname}}
fi

echo Adding user {{printf "%q" .Username}}
useradd -o -g {{.Gid}} -u {{.Uid}} -s /bin/bash -m \
	-c {{printf "%q" .Name}} {{printf "%q" .Username}}

for g in kvm portage sudo; do
	# copy system group from /usr to /etc if needed
	if getent -s usrfiles group "$g" >/dev/null && \
	   ! getent -s files group "$g" >/dev/null; then
		getent -s usrfiles group "$g" >> /etc/group
	fi
	gpasswd -a {{printf "%q" .Username}} "$g"
done

echo Setting up sudoers
cat >/etc/sudoers.d/90_env_keep <<EOF
Defaults env_keep += "\
COREOS_BUILD_ID COREOS_OFFICIAL \
EMAIL GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME \
GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME \
GIT_PROXY_COMMAND GIT_SSH RSYNC_PROXY \
GNUPGHOME GPG_AGENT_INFO SSH_AUTH_SOCK \
USE FEATURES PORTAGE_USERNAME \
all_proxy ftp_proxy http_proxy https_proxy no_proxy"
EOF
chmod 0440 /etc/sudoers.d/90_env_keep

echo Setting default enviornment variables
cat >/etc/env.d/90portage_username <<EOF
PORTAGE_USERNAME={{printf "%q" .Username}}
EOF
# needlessly noisy since portage isn't set up yet
env-update &>/dev/null

echo Setting up home directory
HOME=/home/{{printf "%q" .Username}}

# Create ~/trunk symlink
ln -sfT /mnt/host/source "$HOME"/trunk

rm -f "$HOME"/.bash{_logout,_profile,rc}
cat >"$HOME"/.bash_logout <<EOF
# .bash_logout

# This file is sourced when a login shell terminates.
EOF

cat >"$HOME"/.bash_profile <<EOF
# .bash_profile

# This file is sourced by bash for login shells.  The following line
# runs your .bashrc and is recommended by the bash info pages.
[[ -f ~/.bashrc ]] && . ~/.bashrc

# Automatically change to scripts directory.
cd ${CHROOT_CWD:-~/trunk/src/scripts}
EOF

cat >"$HOME"/.bashrc <<EOF
# .bashrc

# This file is sourced by all *interactive* bash shells on startup,
# including some apparently interactive shells such as scp and rcp
# that can't tolerate any output.  So make sure this doesn't display
# anything or bad things will happen !

# Test for an interactive shell.  There is no need to set anything
# past this point for scp and rcp, and it's important to refrain from
# outputting anything in those cases.
if [[ $- != *i* ]] ; then
	# Shell is non-interactive.  Be done now!
	return
fi

# Enable bash completion for build scripts.
source ~/trunk/src/scripts/bash_completion

# Put your fun stuff here.
EOF

chown -R {{.Uid}}:{{.Gid}} "$HOME"

# Checked in src/scripts/common.sh
touch /etc/debian_chroot
`
)

var scriptTemplate = template.Must(template.New("script").Parse(script))

func Setup(name string) error {
	chroot := filepath.Join(RepoRoot(), name)
	u, err := user.Current()
	if err != nil {
		return err
	}

	var sc bytes.Buffer
	if err := scriptTemplate.Execute(&sc, u); err != nil {
		return err
	}

	plog.Info("Configuring SDK chroot")
	sh := exec.Command(
		"sudo", sudoPrompt,
		"chroot", chroot,
		"/usr/bin/env", "-i",
		"/bin/bash", "--login")
	sh.Stdin = &sc
	sh.Stderr = os.Stderr
	if plog.LevelAt(capnslog.INFO) {
		out, err := sh.StdoutPipe()
		if err != nil {
			return err
		}
		go util.LogFrom(capnslog.INFO, out)
	}
	if plog.LevelAt(capnslog.DEBUG) {
		sh.Args = append(sh.Args, "-x")
	}
	return sh.Run()
}

func extract(tar, dir string) error {
	in, err := os.Open(tar)
	if err != nil {
		return err
	}
	defer in.Close()

	unzipper, err := exec.LookPath("lbzcat")
	if err != nil {
		unzipper = "bzcat"
	}

	unzip := exec.Command(unzipper)
	unzip.Stdin = in
	unzip.Stderr = os.Stderr
	unzipped, err := unzip.StdoutPipe()
	if err != nil {
		return err
	}

	untar := exec.Command("sudo", sudoPrompt,
		"tar", "--numeric-owner", "-x")
	untar.Dir = dir
	untar.Stdin = unzipped
	untar.Stderr = os.Stderr

	if err := unzip.Start(); err != nil {
		return err
	}

	if err := untar.Start(); err != nil {
		unzip.Kill()
		return err
	}

	if err := untar.Wait(); err != nil {
		unzip.Kill()
		return err
	}

	if err := unzip.Wait(); err != nil {
		return err
	}

	return nil
}

func Unpack(version, name string) error {
	chroot := filepath.Join(RepoRoot(), name)
	if _, err := os.Stat(chroot); !os.IsNotExist(err) {
		if err == nil {
			err = fmt.Errorf("Path already exists: %s", chroot)
		}
		return err
	}

	plog.Noticef("Unpacking SDK into %s", chroot)
	if err := os.MkdirAll(chroot, 0777); err != nil {
		return err
	}

	tar := filepath.Join(RepoCache(), "sdks", TarballName(version))
	plog.Infof("Using %s", tar)
	if err := extract(tar, chroot); err != nil {
		plog.Errorf("Extracting %s to %s failed: %v", tar, chroot, err)
		return err
	}
	plog.Notice("Unpacked")

	return nil
}

func Delete(name string) error {
	chroot := filepath.Join(RepoRoot(), name)
	if _, err := os.Stat(chroot); err != nil {
		if os.IsNotExist(err) {
			plog.Infof("Path does not exist: %s", chroot)
			return nil
		}
		return err
	}

	plog.Noticef("Removing SDK at %s", chroot)
	rm := exec.Command("sudo", sudoPrompt, "rm", "-rf", chroot)
	rm.Stderr = os.Stderr
	if err := rm.Run(); err != nil {
		return err
	}
	plog.Notice("Removed")

	return nil
}
<commit_msg>sdk: only add core user if it does not exist<commit_after>// Copyright 2015 CoreOS, Inc.
// Copyright 2011 The Go Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package sdk

import (
	"bytes"
	"fmt"
	"os"
	"path/filepath"
	"text/template"

	"github.com/coreos/mantle/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
	"github.com/coreos/mantle/system/exec"
	"github.com/coreos/mantle/system/user"
	"github.com/coreos/mantle/util"
)

// Must run inside the SDK chroot, easiest to just assemble a script to do it
const (
	safePath   = "PATH=/usr/sbin:/usr/bin:/sbin:/bin"
	sudoPrompt = "--prompt=sudo password for %p: "
	script     = `#!/bin/bash
set -e

# make sure user/group database files exist
touch /etc/{group,gshadow,passwd,shadow}
chmod 0640 /etc/{gshadow,shadow}

# add group if it doesn't exist already
if ! getent group {{printf "%q" .Groupname}} >/dev/null; then
	echo Adding group {{printf "%q" .Groupname}}
	groupadd -o -g {{.Gid}} {{printf "%q" .Groupname}}
fi

# add user if it doesn't exist already
if ! getent passwd {{printf "%q" .Username}} >/dev/null; then
	echo Adding user {{printf "%q" .Username}}
	useradd -o -g {{.Gid}} -u {{.Uid}} -s /bin/bash -m \
		-c {{printf "%q" .Name}} {{printf "%q" .Username}}
fi

for g in kvm portage sudo; do
	# copy system group from /usr to /etc if needed
	if getent -s usrfiles group "$g" >/dev/null && \
	   ! getent -s files group "$g" >/dev/null; then
		getent -s usrfiles group "$g" >> /etc/group
	fi
	gpasswd -a {{printf "%q" .Username}} "$g"
done

echo Setting up sudoers
cat >/etc/sudoers.d/90_env_keep <<EOF
Defaults env_keep += "\
COREOS_BUILD_ID COREOS_OFFICIAL \
EMAIL GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME \
GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME \
GIT_PROXY_COMMAND GIT_SSH RSYNC_PROXY \
GNUPGHOME GPG_AGENT_INFO SSH_AUTH_SOCK \
USE FEATURES PORTAGE_USERNAME \
all_proxy ftp_proxy http_proxy https_proxy no_proxy"
EOF
chmod 0440 /etc/sudoers.d/90_env_keep

echo Setting default enviornment variables
cat >/etc/env.d/90portage_username <<EOF
PORTAGE_USERNAME={{printf "%q" .Username}}
EOF
# needlessly noisy since portage isn't set up yet
env-update &>/dev/null

echo Setting up home directory
HOME=/home/{{printf "%q" .Username}}

# Create ~/trunk symlink
ln -sfT /mnt/host/source "$HOME"/trunk

rm -f "$HOME"/.bash{_logout,_profile,rc}
cat >"$HOME"/.bash_logout <<EOF
# .bash_logout

# This file is sourced when a login shell terminates.
EOF

cat >"$HOME"/.bash_profile <<EOF
# .bash_profile

# This file is sourced by bash for login shells.  The following line
# runs your .bashrc and is recommended by the bash info pages.
[[ -f ~/.bashrc ]] && . ~/.bashrc

# Automatically change to scripts directory.
cd ${CHROOT_CWD:-~/trunk/src/scripts}
EOF

cat >"$HOME"/.bashrc <<EOF
# .bashrc

# This file is sourced by all *interactive* bash shells on startup,
# including some apparently interactive shells such as scp and rcp
# that can't tolerate any output.  So make sure this doesn't display
# anything or bad things will happen !

# Test for an interactive shell.  There is no need to set anything
# past this point for scp and rcp, and it's important to refrain from
# outputting anything in those cases.
if [[ $- != *i* ]] ; then
	# Shell is non-interactive.  Be done now!
	return
fi

# Enable bash completion for build scripts.
source ~/trunk/src/scripts/bash_completion

# Put your fun stuff here.
EOF

chown -R {{.Uid}}:{{.Gid}} "$HOME"

# Checked in src/scripts/common.sh
touch /etc/debian_chroot
`
)

var scriptTemplate = template.Must(template.New("script").Parse(script))

func Setup(name string) error {
	chroot := filepath.Join(RepoRoot(), name)
	u, err := user.Current()
	if err != nil {
		return err
	}

	var sc bytes.Buffer
	if err := scriptTemplate.Execute(&sc, u); err != nil {
		return err
	}

	plog.Info("Configuring SDK chroot")
	sh := exec.Command(
		"sudo", sudoPrompt,
		"chroot", chroot,
		"/usr/bin/env", "-i",
		"/bin/bash", "--login")
	sh.Stdin = &sc
	sh.Stderr = os.Stderr
	if plog.LevelAt(capnslog.INFO) {
		out, err := sh.StdoutPipe()
		if err != nil {
			return err
		}
		go util.LogFrom(capnslog.INFO, out)
	}
	if plog.LevelAt(capnslog.DEBUG) {
		sh.Args = append(sh.Args, "-x")
	}
	return sh.Run()
}

func extract(tar, dir string) error {
	in, err := os.Open(tar)
	if err != nil {
		return err
	}
	defer in.Close()

	unzipper, err := exec.LookPath("lbzcat")
	if err != nil {
		unzipper = "bzcat"
	}

	unzip := exec.Command(unzipper)
	unzip.Stdin = in
	unzip.Stderr = os.Stderr
	unzipped, err := unzip.StdoutPipe()
	if err != nil {
		return err
	}

	untar := exec.Command("sudo", sudoPrompt,
		"tar", "--numeric-owner", "-x")
	untar.Dir = dir
	untar.Stdin = unzipped
	untar.Stderr = os.Stderr

	if err := unzip.Start(); err != nil {
		return err
	}

	if err := untar.Start(); err != nil {
		unzip.Kill()
		return err
	}

	if err := untar.Wait(); err != nil {
		unzip.Kill()
		return err
	}

	if err := unzip.Wait(); err != nil {
		return err
	}

	return nil
}

func Unpack(version, name string) error {
	chroot := filepath.Join(RepoRoot(), name)
	if _, err := os.Stat(chroot); !os.IsNotExist(err) {
		if err == nil {
			err = fmt.Errorf("Path already exists: %s", chroot)
		}
		return err
	}

	plog.Noticef("Unpacking SDK into %s", chroot)
	if err := os.MkdirAll(chroot, 0777); err != nil {
		return err
	}

	tar := filepath.Join(RepoCache(), "sdks", TarballName(version))
	plog.Infof("Using %s", tar)
	if err := extract(tar, chroot); err != nil {
		plog.Errorf("Extracting %s to %s failed: %v", tar, chroot, err)
		return err
	}
	plog.Notice("Unpacked")

	return nil
}

func Delete(name string) error {
	chroot := filepath.Join(RepoRoot(), name)
	if _, err := os.Stat(chroot); err != nil {
		if os.IsNotExist(err) {
			plog.Infof("Path does not exist: %s", chroot)
			return nil
		}
		return err
	}

	plog.Noticef("Removing SDK at %s", chroot)
	rm := exec.Command("sudo", sudoPrompt, "rm", "-rf", chroot)
	rm.Stderr = os.Stderr
	if err := rm.Run(); err != nil {
		return err
	}
	plog.Notice("Removed")

	return nil
}
<|endoftext|>


<commit_before>package gofn

import (
	"bytes"
	"io"

	docker "github.com/fsouza/go-dockerclient"
	"github.com/nuveo/gofn/iaas"
	"github.com/nuveo/gofn/provision"
)

const dockerPort = ":2375"

// Input receives a string that will be written to the stdin of the container
var Input string

// ProvideMachine provisioning a machine in the cloud
func ProvideMachine(service iaas.Iaas) (client *docker.Client, machine *iaas.Machine, err error) {
	machine, err = service.CreateMachine()
	if err != nil {
		if machine != nil {
			err = service.DeleteMachine(machine)
		}
		return
	}
	client, err = provision.FnClient(machine.IP + dockerPort)
	if err != nil {
		return
	}
	return
}

// PrepareContainer build an image if necessary and run the container
func PrepareContainer(client *docker.Client, buildOpts *provision.BuildOptions, volumeOpts *provision.VolumeOptions) (container *docker.Container, err error) {
	img, err := provision.FnFindImage(client, buildOpts.ImageName)
	if err != nil && err != provision.ErrImageNotFound {
		return
	}

	var image string
	if img.ID == "" {
		image, _, err = provision.FnImageBuild(client, buildOpts)
		if err != nil {
			return
		}
	} else {
		image = "gofn/" + buildOpts.ImageName
	}

	volume := ""
	if volumeOpts != nil {
		volume = provision.FnConfigVolume(volumeOpts)
	}

	container, err = provision.FnContainer(client, image, volume)
	if err != nil {
		return
	}
	return
}

// RunWait runs the conainer returning channels to control your status
func RunWait(client *docker.Client, container *docker.Container) (err error, running chan bool, errors chan error) {
	err = provision.FnStart(client, container.ID)
	if err != nil {
		return
	}
	running, errors = provision.FnWaitContainer(client, container.ID)
	return
}

// Attach allow to connect into a running container and interact using stdout, stderr and stdin
func Attach(client *docker.Client, container *docker.Container, stdin io.Reader, stdout io.Writer, stderr io.Writer) (docker.CloseWaiter, error) {
	return provision.FnAttach(client, container.ID, stdin, stdout, stderr)
}

// Run runs the designed image
func Run(buildOpts *provision.BuildOptions, volumeOpts *provision.VolumeOptions) (stdout string, stderr string, err error) {
	var client *docker.Client
	client, err = provision.FnClient("")
	if err != nil {
		return
	}

	if buildOpts.Iaas != nil {
		var machine *iaas.Machine
		client, machine, err = ProvideMachine(buildOpts.Iaas)
		if err != nil {
			return
		}
		defer buildOpts.Iaas.DeleteMachine(machine)
	}

	var container *docker.Container
	container, err = PrepareContainer(client, buildOpts, volumeOpts)

	var buffout *bytes.Buffer
	var bufferr *bytes.Buffer

	provision.Input = Input
	buffout, bufferr, err = provision.FnRun(client, container.ID)
	if err != nil {
		return
	}
	stdout = buffout.String()
	stderr = bufferr.String()

	err = DestroyContainer(client, container)
	return

}

func DestroyContainer(client *docker.Client, container *docker.Container) error {
	return provision.FnRemove(client, container.ID)
}
<commit_msg>Used GetImageName() on gofn.Run<commit_after>package gofn

import (
	"bytes"
	"io"

	docker "github.com/fsouza/go-dockerclient"
	"github.com/nuveo/gofn/iaas"
	"github.com/nuveo/gofn/provision"
)

const dockerPort = ":2375"

// Input receives a string that will be written to the stdin of the container
var Input string

// ProvideMachine provisioning a machine in the cloud
func ProvideMachine(service iaas.Iaas) (client *docker.Client, machine *iaas.Machine, err error) {
	machine, err = service.CreateMachine()
	if err != nil {
		if machine != nil {
			err = service.DeleteMachine(machine)
		}
		return
	}
	client, err = provision.FnClient(machine.IP + dockerPort)
	if err != nil {
		return
	}
	return
}

// PrepareContainer build an image if necessary and run the container
func PrepareContainer(client *docker.Client, buildOpts *provision.BuildOptions, volumeOpts *provision.VolumeOptions) (container *docker.Container, err error) {
	img, err := provision.FnFindImage(client, buildOpts.GetImageName())
	if err != nil && err != provision.ErrImageNotFound {
		return
	}

	var image string
	if img.ID == "" {
		image, _, err = provision.FnImageBuild(client, buildOpts)
		if err != nil {
			return
		}
	} else {
		image = buildOpts.GetImageName()
	}

	volume := ""
	if volumeOpts != nil {
		volume = provision.FnConfigVolume(volumeOpts)
	}

	container, err = provision.FnContainer(client, image, volume)
	if err != nil {
		return
	}
	return
}

// RunWait runs the conainer returning channels to control your status
func RunWait(client *docker.Client, container *docker.Container) (err error, running chan bool, errors chan error) {
	err = provision.FnStart(client, container.ID)
	if err != nil {
		return
	}
	running, errors = provision.FnWaitContainer(client, container.ID)
	return
}

// Attach allow to connect into a running container and interact using stdout, stderr and stdin
func Attach(client *docker.Client, container *docker.Container, stdin io.Reader, stdout io.Writer, stderr io.Writer) (docker.CloseWaiter, error) {
	return provision.FnAttach(client, container.ID, stdin, stdout, stderr)
}

// Run runs the designed image
func Run(buildOpts *provision.BuildOptions, volumeOpts *provision.VolumeOptions) (stdout string, stderr string, err error) {
	var client *docker.Client
	client, err = provision.FnClient("")
	if err != nil {
		return
	}

	if buildOpts.Iaas != nil {
		var machine *iaas.Machine
		client, machine, err = ProvideMachine(buildOpts.Iaas)
		if err != nil {
			return
		}
		defer buildOpts.Iaas.DeleteMachine(machine)
	}

	var container *docker.Container
	container, err = PrepareContainer(client, buildOpts, volumeOpts)

	var buffout *bytes.Buffer
	var bufferr *bytes.Buffer

	provision.Input = Input
	buffout, bufferr, err = provision.FnRun(client, container.ID)
	if err != nil {
		return
	}
	stdout = buffout.String()
	stderr = bufferr.String()

	err = DestroyContainer(client, container)
	return

}

func DestroyContainer(client *docker.Client, container *docker.Container) error {
	return provision.FnRemove(client, container.ID)
}
<|endoftext|>