<commit_before>// Basic sequence package
package seq
// Copyright ©2011 Dan Kortschak <dan.kortschak@adelaide.edu.au>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

import (
	"github.com/kortschak/BioGo/bio"
	"github.com/kortschak/BioGo/feat"
	"github.com/kortschak/BioGo/interval"
	"github.com/kortschak/BioGo/util"
)

const (
	Prepend = iota
	Append
)

var (
	complement = [...]map[byte]byte{
		{'a': 't', 'c': 'g', 'g': 'c', 't': 'a', 'n': 'n',
			'A': 'T', 'C': 'G', 'G': 'C', 'T': 'A', 'N': 'N'}, // DNA rules
		{'a': 'u', 'c': 'g', 'g': 'c', 'u': 'a', 'n': 'n',
			'A': 'U', 'C': 'G', 'G': 'C', 'U': 'A', 'N': 'N'}, // RNA rules
	}
)

type Seq struct {
	ID       string
	Seq      []byte
	Offset   int
	Strand   int8
	Circular bool
	Moltype  bio.Moltype
	Quality  *Quality
	Inplace  bool
	Meta     interface{} // No operation on Seq objects implicitly copies or changes the contents of Meta.
}

func New(id string, seq []byte, qual *Quality) *Seq {
	return &Seq{
		ID:       id,
		Seq:      seq,
		Offset:   0,
		Strand:   1,
		Circular: false,
		Moltype:  0,
		Quality:  qual,
		Inplace:  false,
	}
}

// Set Inplace and return self for chaining.
func (self *Seq) WorkInplace(b bool) *Seq {
	self.Inplace = b
	return self
}

func (self *Seq) Len() int {
	return len(self.Seq)
}

func (self *Seq) Start() int {
	return self.Offset
}

func (self *Seq) End() int {
	return self.Offset + len(self.Seq)
}

func (self *Seq) Trunc(start, end int) (s *Seq, err error) {
	var ts []byte

	if !self.Inplace && self.Quality != nil && self.Quality.Inplace {
		return nil, bio.NewError("Inplace operation on Quality with non-Inplace operation on parent Seq.", 0, self)
	}

	if start < self.Offset || end < self.Offset ||
		start > len(self.Seq)+self.Offset || end > len(self.Seq)+self.Offset {
		return nil, bio.NewError("Start or end position out of range.", 0, self)
	}

	if start <= end {
		if self.Inplace {
			ts = self.Seq[start-self.Offset : end-self.Offset]
		} else {
			ts = append([]byte{}, self.Seq[start-self.Offset:end-self.Offset]...)
		}
	} else if self.Circular {
		if self.Inplace {
			ts = append(self.Seq[start-self.Offset:], self.Seq[:end-self.Offset]...) // not quite inplace for this op
		} else {
			ts = make([]byte, len(self.Seq)-start-self.Offset, len(self.Seq)+end-start)
			copy(ts, self.Seq[start-self.Offset:])
			ts = append(ts, self.Seq[:end-self.Offset]...)
		}
	} else {
		return nil, bio.NewError("Start position greater than end position for non-circular molecule.", 0, self)
	}

	var q *Quality
	if self.Quality != nil {
		if q, err = self.Quality.Trunc(start, end); err != nil {
			err = bio.NewError("Quality.Trunc() returned error", 0, err)
			return
		}
	}

	if self.Inplace {
		s = self
		s.Seq = ts
		s.Circular = false
		s.Quality = q
	} else {
		s = &Seq{
			ID:       self.ID,
			Seq:      ts,
			Offset:   start,
			Strand:   self.Strand,
			Circular: false,
			Moltype:  self.Moltype,
			Quality:  q,
		}
	}

	return
}

func (self *Seq) RevComp() (s *Seq, err error) {
	var rs []byte
	if self.Inplace {
		rs = self.Seq
	} else {
		if self.Quality != nil && self.Quality.Inplace {
			return nil, bio.NewError("Inplace operation on Quality with non-Inplace operation on parent Seq.", 0, self)
		}
		rs = make([]byte, len(self.Seq))
	}

	if self.Moltype == bio.DNA || self.Moltype == bio.RNA {
		i, j := 0, len(self.Seq)-1
		for ; i < j; i, j = i+1, j-1 {
			rs[i], rs[j] = complement[self.Moltype][self.Seq[j]], complement[self.Moltype][self.Seq[i]]
		}
		if i == j {
			rs[i] = complement[self.Moltype][self.Seq[i]]
		}
	} else {
		return nil, bio.NewError("Cannot reverse-complement protein.", 0, self)
	}

	var q *Quality
	if self.Quality != nil {
		q = self.Quality.Reverse()
	}

	if self.Inplace {
		s = self
		s.Quality = q
	} else {
		s = &Seq{
			ID:       self.ID,
			Seq:      rs,
			Offset:   self.Offset + len(self.Seq),
			Strand:   -self.Strand,
			Circular: self.Circular,
			Moltype:  self.Moltype,
			Quality:  q,
		}
	}

	return
}

func (self *Seq) Join(s *Seq, where int) (j *Seq, err error) {
	var (
		ts []byte
		ID string
	)

	if self.Circular {
		return nil, bio.NewError("Cannot join circular molecule.", 0, self)
	}

	if !self.Inplace && self.Quality != nil && self.Quality.Inplace {
		return nil, bio.NewError("Inplace operation on Quality with non-Inplace operation on parent Seq.", 0, self)
	}

	switch where {
	case Prepend:
		ID = s.ID + "+" + self.ID
		ts = make([]byte, len(s.Seq), len(s.Seq)+len(self.Seq))
		copy(ts, s.Seq)
		ts = append(ts, self.Seq...)
	case Append:
		ID = self.ID + "+" + s.ID
		if self.Inplace {
			ts = append(self.Seq, s.Seq...)
		} else {
			ts = make([]byte, len(self.Seq), len(s.Seq)+len(self.Seq))
			copy(ts, self.Seq)
			ts = append(ts, s.Seq...)
		}
	}

	var q *Quality
	if self.Quality != nil && s.Quality != nil {
		if q, err = self.Quality.Join(s.Quality, where); err != nil {
			return nil, err
		}
	}

	if self.Inplace {
		j = self
		j.Seq = ts
		j.Quality = q // self.Quality will become nil if either sequence lacks Quality
	} else {
		j = &Seq{
			ID:      ID,
			Seq:     ts,
			Strand:  self.Strand,
			Moltype: self.Moltype,
			Quality: q,
		}
	}
	if where == Prepend {
		j.Offset -= s.Len()
	}

	return
}

func (self *Seq) Stitch(f feat.FeatureSet) (s *Seq, err error) {
	if !self.Inplace && self.Quality != nil && self.Quality.Inplace {
		return nil, bio.NewError("Inplace operation on Quality with non-Inplace operation on parent Seq.", 0, self)
	}

	t := interval.NewTree()
	var i *interval.Interval

	for _, feature := range f {
		if i, err = interval.New("", feature.Start, feature.End, 0, nil); err != nil {
			return nil, err
		} else {
			t.Insert(i)
		}
	}

	span, err := interval.New("", self.Start(), self.End(), 0, nil)
	if err != nil {
		panic("Seq.End() < Seq.Start()")
	}
	fs, _ := t.Flatten(span, 0, 0)

	if self.Inplace {
		self.Seq = self.stitch(fs)
		self.Offset = 0
		self.Circular = false
		if self.Quality != nil {
			var q *Quality
			if !self.Quality.Inplace {
				q = &Quality{ID: self.Quality.ID}
			}
			q.Qual = self.Quality.stitch(fs)
			q.Offset = 0
			q.Circular = false
			self.Quality = q
		}
		s = self
	} else {
		var q *Quality
		if self.Quality != nil {
			q = &Quality{
				ID:       self.Quality.ID,
				Qual:     self.Quality.stitch(fs),
				Offset:   0,
				Circular: false,
			}
		}
		s = &Seq{
			ID:       self.ID,
			Seq:      self.stitch(fs),
			Offset:   0,
			Strand:   self.Strand,
			Circular: false,
			Moltype:  self.Moltype,
			Quality:  q,
		}
	}

	return
}

func (self *Seq) stitch(f []*interval.Interval) (ts []byte) {
	for _, seg := range f {
		ts = append(ts, self.Seq[util.Max(seg.Start()-self.Offset, 0):util.Min(seg.End()-self.Offset, len(self.Seq))]...)
	}

	return
}

var defaultStringFunc = func(s *Seq) string { return string(s.Seq) }

var StringFunc = defaultStringFunc

func (self *Seq) String() string {
	return StringFunc(self)
}
<commit_msg>Reflect join in ID.<commit_after>// Basic sequence package
package seq
// Copyright ©2011 Dan Kortschak <dan.kortschak@adelaide.edu.au>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

import (
	"github.com/kortschak/BioGo/bio"
	"github.com/kortschak/BioGo/feat"
	"github.com/kortschak/BioGo/interval"
	"github.com/kortschak/BioGo/util"
)

const (
	Prepend = iota
	Append
)

var (
	complement = [...]map[byte]byte{
		{'a': 't', 'c': 'g', 'g': 'c', 't': 'a', 'n': 'n',
			'A': 'T', 'C': 'G', 'G': 'C', 'T': 'A', 'N': 'N'}, // DNA rules
		{'a': 'u', 'c': 'g', 'g': 'c', 'u': 'a', 'n': 'n',
			'A': 'U', 'C': 'G', 'G': 'C', 'U': 'A', 'N': 'N'}, // RNA rules
	}
)

type Seq struct {
	ID       string
	Seq      []byte
	Offset   int
	Strand   int8
	Circular bool
	Moltype  bio.Moltype
	Quality  *Quality
	Inplace  bool
	Meta     interface{} // No operation on Seq objects implicitly copies or changes the contents of Meta.
}

func New(id string, seq []byte, qual *Quality) *Seq {
	return &Seq{
		ID:       id,
		Seq:      seq,
		Offset:   0,
		Strand:   1,
		Circular: false,
		Moltype:  0,
		Quality:  qual,
		Inplace:  false,
	}
}

// Set Inplace and return self for chaining.
func (self *Seq) WorkInplace(b bool) *Seq {
	self.Inplace = b
	return self
}

func (self *Seq) Len() int {
	return len(self.Seq)
}

func (self *Seq) Start() int {
	return self.Offset
}

func (self *Seq) End() int {
	return self.Offset + len(self.Seq)
}

func (self *Seq) Trunc(start, end int) (s *Seq, err error) {
	var ts []byte

	if !self.Inplace && self.Quality != nil && self.Quality.Inplace {
		return nil, bio.NewError("Inplace operation on Quality with non-Inplace operation on parent Seq.", 0, self)
	}

	if start < self.Offset || end < self.Offset ||
		start > len(self.Seq)+self.Offset || end > len(self.Seq)+self.Offset {
		return nil, bio.NewError("Start or end position out of range.", 0, self)
	}

	if start <= end {
		if self.Inplace {
			ts = self.Seq[start-self.Offset : end-self.Offset]
		} else {
			ts = append([]byte{}, self.Seq[start-self.Offset:end-self.Offset]...)
		}
	} else if self.Circular {
		if self.Inplace {
			ts = append(self.Seq[start-self.Offset:], self.Seq[:end-self.Offset]...) // not quite inplace for this op
		} else {
			ts = make([]byte, len(self.Seq)-start-self.Offset, len(self.Seq)+end-start)
			copy(ts, self.Seq[start-self.Offset:])
			ts = append(ts, self.Seq[:end-self.Offset]...)
		}
	} else {
		return nil, bio.NewError("Start position greater than end position for non-circular molecule.", 0, self)
	}

	var q *Quality
	if self.Quality != nil {
		if q, err = self.Quality.Trunc(start, end); err != nil {
			err = bio.NewError("Quality.Trunc() returned error", 0, err)
			return
		}
	}

	if self.Inplace {
		s = self
		s.Seq = ts
		s.Circular = false
		s.Quality = q
	} else {
		s = &Seq{
			ID:       self.ID,
			Seq:      ts,
			Offset:   start,
			Strand:   self.Strand,
			Circular: false,
			Moltype:  self.Moltype,
			Quality:  q,
		}
	}

	return
}

func (self *Seq) RevComp() (s *Seq, err error) {
	var rs []byte
	if self.Inplace {
		rs = self.Seq
	} else {
		if self.Quality != nil && self.Quality.Inplace {
			return nil, bio.NewError("Inplace operation on Quality with non-Inplace operation on parent Seq.", 0, self)
		}
		rs = make([]byte, len(self.Seq))
	}

	if self.Moltype == bio.DNA || self.Moltype == bio.RNA {
		i, j := 0, len(self.Seq)-1
		for ; i < j; i, j = i+1, j-1 {
			rs[i], rs[j] = complement[self.Moltype][self.Seq[j]], complement[self.Moltype][self.Seq[i]]
		}
		if i == j {
			rs[i] = complement[self.Moltype][self.Seq[i]]
		}
	} else {
		return nil, bio.NewError("Cannot reverse-complement protein.", 0, self)
	}

	var q *Quality
	if self.Quality != nil {
		q = self.Quality.Reverse()
	}

	if self.Inplace {
		s = self
		s.Quality = q
	} else {
		s = &Seq{
			ID:       self.ID,
			Seq:      rs,
			Offset:   self.Offset + len(self.Seq),
			Strand:   -self.Strand,
			Circular: self.Circular,
			Moltype:  self.Moltype,
			Quality:  q,
		}
	}

	return
}

func (self *Seq) Join(s *Seq, where int) (j *Seq, err error) {
	var (
		ts []byte
		ID string
	)

	if self.Circular {
		return nil, bio.NewError("Cannot join circular molecule.", 0, self)
	}

	if !self.Inplace && self.Quality != nil && self.Quality.Inplace {
		return nil, bio.NewError("Inplace operation on Quality with non-Inplace operation on parent Seq.", 0, self)
	}

	switch where {
	case Prepend:
		ID = s.ID + "+" + self.ID
		ts = make([]byte, len(s.Seq), len(s.Seq)+len(self.Seq))
		copy(ts, s.Seq)
		ts = append(ts, self.Seq...)
	case Append:
		ID = self.ID + "+" + s.ID
		if self.Inplace {
			ts = append(self.Seq, s.Seq...)
		} else {
			ts = make([]byte, len(self.Seq), len(s.Seq)+len(self.Seq))
			copy(ts, self.Seq)
			ts = append(ts, s.Seq...)
		}
	}

	var q *Quality
	if self.Quality != nil && s.Quality != nil {
		if q, err = self.Quality.Join(s.Quality, where); err != nil {
			return nil, err
		}
	}

	if self.Inplace {
		j = self
		j.ID = ID
		j.Seq = ts
		j.Quality = q // self.Quality will become nil if either sequence lacks Quality
	} else {
		j = &Seq{
			ID:      ID,
			Seq:     ts,
			Strand:  self.Strand,
			Moltype: self.Moltype,
			Quality: q,
		}
	}
	if where == Prepend {
		j.Offset -= s.Len()
	}

	return
}

func (self *Seq) Stitch(f feat.FeatureSet) (s *Seq, err error) {
	if !self.Inplace && self.Quality != nil && self.Quality.Inplace {
		return nil, bio.NewError("Inplace operation on Quality with non-Inplace operation on parent Seq.", 0, self)
	}

	t := interval.NewTree()
	var i *interval.Interval

	for _, feature := range f {
		if i, err = interval.New("", feature.Start, feature.End, 0, nil); err != nil {
			return nil, err
		} else {
			t.Insert(i)
		}
	}

	span, err := interval.New("", self.Start(), self.End(), 0, nil)
	if err != nil {
		panic("Seq.End() < Seq.Start()")
	}
	fs, _ := t.Flatten(span, 0, 0)

	if self.Inplace {
		self.Seq = self.stitch(fs)
		self.Offset = 0
		self.Circular = false
		if self.Quality != nil {
			var q *Quality
			if !self.Quality.Inplace {
				q = &Quality{ID: self.Quality.ID}
			}
			q.Qual = self.Quality.stitch(fs)
			q.Offset = 0
			q.Circular = false
			self.Quality = q
		}
		s = self
	} else {
		var q *Quality
		if self.Quality != nil {
			q = &Quality{
				ID:       self.Quality.ID,
				Qual:     self.Quality.stitch(fs),
				Offset:   0,
				Circular: false,
			}
		}
		s = &Seq{
			ID:       self.ID,
			Seq:      self.stitch(fs),
			Offset:   0,
			Strand:   self.Strand,
			Circular: false,
			Moltype:  self.Moltype,
			Quality:  q,
		}
	}

	return
}

func (self *Seq) stitch(f []*interval.Interval) (ts []byte) {
	for _, seg := range f {
		ts = append(ts, self.Seq[util.Max(seg.Start()-self.Offset, 0):util.Min(seg.End()-self.Offset, len(self.Seq))]...)
	}

	return
}

var defaultStringFunc = func(s *Seq) string { return string(s.Seq) }

var StringFunc = defaultStringFunc

func (self *Seq) String() string {
	return StringFunc(self)
}
<|endoftext|>


<commit_before>package config

import (
	"github.com/mmmorris1975/aws-config/config"
	"github.com/mmmorris1975/aws-runas/lib/credentials"
	"github.com/mmmorris1975/simple-logger"
	"os"
	"testing"
	"time"
)

func TestNewConfigResolver(t *testing.T) {
	os.Setenv(config.ConfigFileEnvVar, "../../.aws/config")
	t.Run("nil config", func(t *testing.T) {
		r, err := NewConfigResolver(nil)
		if err != nil {
			t.Error(err)
			return
		}

		if r == nil {
			t.Error("unexpected nil resolver")
			return
		}

		if r.file == nil || len(r.file.Path) < 1 {
			t.Error("config file not set")
		}
	})

	t.Run("good", func(t *testing.T) {
		r, err := NewConfigResolver(&AwsConfig{Region: "us-west-2"})
		if err != nil {
			t.Error(err)
			return
		}

		if r.userConfig.Region != "us-west-2" {
			t.Error("unexpected region")
		}
	})

	t.Run("config file env var", func(t *testing.T) {
		os.Setenv(config.ConfigFileEnvVar, "test/config")
		defer os.Unsetenv(config.ConfigFileEnvVar)

		r, err := NewConfigResolver(nil)
		if err != nil {
			t.Error(err)
			return
		}

		if r.file == nil || r.file.Path != "test/config" {
			t.Error("bad file name")
		}
	})
}

func TestConfigResolver_ResolveConfig(t *testing.T) {
	os.Setenv(config.ConfigFileEnvVar, "test/config")
	defer os.Unsetenv(config.ConfigFileEnvVar)

	t.Run("default only", func(t *testing.T) {
		r, err := NewConfigResolver(nil)
		if err != nil {
			t.Error(err)
			return
		}

		c, err := r.ResolveConfig("")
		if err != nil {
			t.Error(err)
			return
		}

		if c.Region != "us-west-1" {
			t.Error("unexpected region")
		}

		if len(c.MfaSerial) > 0 {
			t.Error("unexpected mfa serial")
		}

		if len(c.RoleArn) > 0 {
			t.Error("unexpected role value")
		}

		if c.SessionDuration != credentials.SessionTokenDefaultDuration {
			t.Error("unexpected session duration")
		}

		if c.RoleDuration != credentials.AssumeRoleDefaultDuration {
			t.Error("unexpected role duration")
		}
	})

	t.Run("source", func(t *testing.T) {
		r, err := NewConfigResolver(nil)
		if err != nil {
			t.Error(err)
			return
		}

		c, err := r.ResolveConfig("source")
		if err != nil {
			t.Error(err)
			return
		}

		if c.Region != "us-east-1" {
			t.Error("unexpected region")
		}

		if c.MfaSerial != "ABCDEFG" {
			t.Error("unexpected mfa serial value")
		}

		if len(c.RoleArn) > 0 {
			t.Error("unexpected role value")
		}

		if c.SessionDuration == 0 {
			t.Error("unexpected session duration")
		}

		if c.RoleDuration != credentials.AssumeRoleDefaultDuration {
			t.Error("unexpected role duration")
		}
	})

	t.Run("role default source", func(t *testing.T) {
		r, err := NewConfigResolver(nil)
		if err != nil {
			t.Error(err)
			return
		}

		c, err := r.ResolveConfig("role1")
		if err != nil {
			t.Error(err)
			return
		}

		if c.Region != "us-west-1" {
			t.Error("unexpected region")
		}

		if len(c.MfaSerial) > 0 {
			t.Error("unexpected mfa serial")
		}

		if c.RoleArn != "role1" {
			t.Error("unexpected role value")
		}

		if c.SessionDuration != credentials.SessionTokenDefaultDuration {
			t.Error("unexpected session duration")
		}

		if c.RoleDuration != credentials.AssumeRoleDefaultDuration {
			t.Error("unexpected role duration")
		}
	})

	t.Run("role non-default source", func(t *testing.T) {
		r, err := NewConfigResolver(nil)
		if err != nil {
			t.Error(err)
			return
		}

		c, err := r.ResolveConfig("role2")
		if err != nil {
			t.Error(err)
			return
		}

		if c.Region != "us-east-1" {
			t.Error("unexpected region")
		}

		if c.MfaSerial != "ABCDEFG" {
			t.Error("unexpected mfa serial value")
		}

		if c.RoleArn != "role2" {
			t.Error("unexpected role value")
		}

		if c.SessionDuration == 0 {
			t.Error("unexpected session duration")
		}

		if c.RoleDuration != credentials.AssumeRoleDefaultDuration {
			t.Error("unexpected role duration")
		}
	})

	t.Run("role bad source", func(t *testing.T) {
		r, err := NewConfigResolver(nil)
		if err != nil {
			t.Error(err)
			return
		}

		c, err := r.ResolveConfig("role3")
		if err != nil {
			t.Error(err)
			return
		}

		if c.SourceProfile != "other" {
			t.Error("source profile mismatch")
		}
	})

	t.Run("arn", func(t *testing.T) {
		os.Setenv(MfaSerialEnvVar, "654321")
		defer os.Unsetenv(MfaSerialEnvVar)

		arn := "arn:aws:iam::0123456789012:role/Admin"

		r, err := NewConfigResolver(nil)
		if err != nil {
			t.Error(err)
			return
		}

		c, err := r.ResolveConfig(arn)
		if err != nil {
			t.Error(err)
			return
		}

		if c.Region != "us-west-1" {
			t.Error("unexpected region")
		}

		if c.RoleArn != arn {
			t.Error("bad role value")
		}

		if c.MfaSerial != "654321" {
			t.Error("bad mfa value")
		}
	})

	t.Run("bad arn", func(t *testing.T) {

		arn := "arn:aws:iam::0123456789012:user/Admin"

		r, err := NewConfigResolver(nil)
		if err != nil {
			t.Error(err)
			return
		}

		_, err = r.ResolveConfig(arn)
		if err == nil {
			t.Error("did not receive expected error")
			return
		}
	})
}

func TestConfigResolver_ResolveDefaultConfig(t *testing.T) {
	os.Setenv(config.ConfigFileEnvVar, "test/config")
	defer os.Unsetenv(config.ConfigFileEnvVar)

	r, err := NewConfigResolver(nil)
	if err != nil {
		t.Error(err)
		return
	}

	t.Run("default", func(t *testing.T) {
		c, err := r.ResolveDefaultConfig()
		if err != nil {
			t.Error(err)
			return
		}

		if c.Region != "us-west-1" {
			t.Error("unexpected region")
		}

		if len(c.MfaSerial) > 0 {
			t.Error("mfa serial was set for default profile")
		}
	})

	t.Run("good env var", func(t *testing.T) {
		os.Setenv(DefaultProfileEnvVar, "source")
		defer os.Unsetenv(DefaultProfileEnvVar)

		c, err := r.ResolveDefaultConfig()
		if err != nil {
			t.Error(err)
			return
		}

		if c.Region != "us-east-1" {
			t.Error("unexpected region")
		}

		if len(c.MfaSerial) > 0 {
			t.Error("mfa serial was set for default profile")
		}

		if c.SessionDuration == 0 {
			t.Error("bad session duration")
		}
	})

	t.Run("bad env var", func(t *testing.T) {
		os.Setenv(DefaultProfileEnvVar, "bad")
		defer os.Unsetenv(DefaultProfileEnvVar)

		_, err := r.ResolveDefaultConfig()
		if err == nil {
			t.Error("did not receive expected error")
			return
		}
	})
}

func TestConfigResolver_ResolveProfileConfig(t *testing.T) {
	os.Setenv(config.ConfigFileEnvVar, "test/config")
	defer os.Unsetenv(config.ConfigFileEnvVar)

	r, err := NewConfigResolver(nil)
	if err != nil {
		t.Error(err)
		return
	}

	t.Run("profile exists", func(t *testing.T) {
		c, err := r.ResolveProfileConfig("role1")
		if err != nil {
			t.Error(err)
			return
		}

		if c.RoleArn != "role1" {
			t.Error("role mismatch")
		}
	})

	t.Run("empty profile", func(t *testing.T) {
		// Will look up default profile
		_, err := r.ResolveProfileConfig("")
		if err != nil {
			t.Error(err)
			return
		}
	})

	t.Run("bad profile", func(t *testing.T) {
		_, err := r.ResolveProfileConfig("bogus")
		if err == nil {
			t.Error("did not receive expected error")
			return
		}
	})
}

func TestConfigResolver_ResolveEnvConfig(t *testing.T) {
	env := make(map[string]string)
	env[RegionEnvVar] = "us-east-2"
	env[ProfileEnvVar] = "profile"
	env[SessionDurationEnvVar] = "1m"
	env[config.CredentialsFileEnvVar] = "test/config"

	for k, v := range env {
		os.Setenv(k, v)
		defer os.Unsetenv(k)
	}

	r, err := NewConfigResolver(nil)
	if err != nil {
		t.Error(err)
		return
	}

	t.Run("good", func(t *testing.T) {
		c, err := r.ResolveEnvConfig()
		if err != nil {
			t.Error(err)
			return
		}

		if c.Region != env[RegionEnvVar] {
			t.Error("bad region")
		}

		if c.SessionDuration != 1*time.Minute {
			t.Error("bad session duration")
		}

		if len(c.RoleArn) > 0 {
			t.Error("unexpected role arn")
		}
	})

	t.Run("bad session duration", func(t *testing.T) {
		os.Setenv(SessionDurationEnvVar, "ab")
		defer os.Unsetenv(SessionDurationEnvVar)

		_, err := r.ResolveEnvConfig()
		if err == nil {
			t.Error("did not see expected error")
			return
		}
	})

	t.Run("bad role duration", func(t *testing.T) {
		os.Setenv(RoleDurationEnvVar, "ab")
		defer os.Unsetenv(RoleDurationEnvVar)

		_, err := r.ResolveEnvConfig()
		if err == nil {
			t.Error("did not see expected error")
			return
		}
	})
}

func TestMergeConfig(t *testing.T) {
	t.Run("all nil", func(t *testing.T) {
		c := MergeConfig(nil)
		if c == nil {
			t.Error("nil config")
			return
		}

		if len(c.Region) > 0 {
			t.Error("unexpected region value")
		}

		if len(c.RoleArn) > 0 {
			t.Error("unexpected role arn")
		}

		if len(c.MfaSerial) > 0 {
			t.Error("unexpected mfa serial")
		}

		if len(c.ExternalID) > 0 {
			t.Error("unexpected external id")
		}

		if c.SessionDuration > 0 {
			t.Error("unexpeted session duration")
		}

		if c.RoleDuration > 0 {
			t.Error("unexpected role duration")
		}
	})

	t.Run("good", func(t *testing.T) {
		c := MergeConfig(
			nil,
			&AwsConfig{Region: "us-east-1"},
			&AwsConfig{MfaSerial: "123456"},
			nil,
			&AwsConfig{Region: "us-east-2", RoleArn: "my-role"})

		if c == nil {
			t.Error("unexpected nil config")
			return
		}

		if c.Region != "us-east-2" {
			t.Error("bad region")
		}

		if c.MfaSerial != "123456" {
			t.Error("bad mfa serial")
		}

		if c.RoleArn != "my-role" {
			t.Error("bad role")
		}
	})
}

func TestConfigResolver_WithLogger(t *testing.T) {
	r := new(configResolver).WithLogger(simple_logger.StdLogger)
	if r.log == nil {
		t.Error("unexpected nil logger")
	}
}

func ExampleDebugNilLogger() {
	r := new(configResolver)
	r.debug("test")
	// Output:
	//
}

func ExampleDebug() {
	l := simple_logger.NewLogger(os.Stdout, "", 0)
	l.SetLevel(simple_logger.DEBUG)
	r := new(configResolver).WithLogger(l)
	r.debug("test")
	// Output:
	// DEBUG test
}
<commit_msg>one more config_test fix to appease the circleci gods<commit_after>package config

import (
	"github.com/mmmorris1975/aws-config/config"
	"github.com/mmmorris1975/aws-runas/lib/credentials"
	"github.com/mmmorris1975/simple-logger"
	"os"
	"testing"
	"time"
)

func TestNewConfigResolver(t *testing.T) {
	os.Setenv(config.ConfigFileEnvVar, "../../.aws/config")
	t.Run("nil config", func(t *testing.T) {
		r, err := NewConfigResolver(nil)
		if err != nil {
			t.Error(err)
			return
		}

		if r == nil {
			t.Error("unexpected nil resolver")
			return
		}

		if r.file == nil || len(r.file.Path) < 1 {
			t.Error("config file not set")
		}
	})

	t.Run("good", func(t *testing.T) {
		r, err := NewConfigResolver(&AwsConfig{Region: "us-west-2"})
		if err != nil {
			t.Error(err)
			return
		}

		if r.userConfig.Region != "us-west-2" {
			t.Error("unexpected region")
		}
	})

	t.Run("config file env var", func(t *testing.T) {
		os.Setenv(config.ConfigFileEnvVar, "test/config")
		defer os.Unsetenv(config.ConfigFileEnvVar)

		r, err := NewConfigResolver(nil)
		if err != nil {
			t.Error(err)
			return
		}

		if r.file == nil || r.file.Path != "test/config" {
			t.Error("bad file name")
		}
	})
}

func TestConfigResolver_ResolveConfig(t *testing.T) {
	os.Setenv(config.ConfigFileEnvVar, "test/config")
	defer os.Unsetenv(config.ConfigFileEnvVar)

	t.Run("default only", func(t *testing.T) {
		r, err := NewConfigResolver(nil)
		if err != nil {
			t.Error(err)
			return
		}

		c, err := r.ResolveConfig("")
		if err != nil {
			t.Error(err)
			return
		}

		if c.Region != "us-west-1" {
			t.Error("unexpected region")
		}

		if len(c.MfaSerial) > 0 {
			t.Error("unexpected mfa serial")
		}

		if len(c.RoleArn) > 0 {
			t.Error("unexpected role value")
		}

		if c.SessionDuration != credentials.SessionTokenDefaultDuration {
			t.Error("unexpected session duration")
		}

		if c.RoleDuration != credentials.AssumeRoleDefaultDuration {
			t.Error("unexpected role duration")
		}
	})

	t.Run("source", func(t *testing.T) {
		r, err := NewConfigResolver(nil)
		if err != nil {
			t.Error(err)
			return
		}

		c, err := r.ResolveConfig("source")
		if err != nil {
			t.Error(err)
			return
		}

		if c.Region != "us-east-1" {
			t.Error("unexpected region")
		}

		if c.MfaSerial != "ABCDEFG" {
			t.Error("unexpected mfa serial value")
		}

		if len(c.RoleArn) > 0 {
			t.Error("unexpected role value")
		}

		if c.SessionDuration == 0 {
			t.Error("unexpected session duration")
		}

		if c.RoleDuration != credentials.AssumeRoleDefaultDuration {
			t.Error("unexpected role duration")
		}
	})

	t.Run("role default source", func(t *testing.T) {
		r, err := NewConfigResolver(nil)
		if err != nil {
			t.Error(err)
			return
		}

		c, err := r.ResolveConfig("role1")
		if err != nil {
			t.Error(err)
			return
		}

		if c.Region != "us-west-1" {
			t.Error("unexpected region")
		}

		if len(c.MfaSerial) > 0 {
			t.Error("unexpected mfa serial")
		}

		if c.RoleArn != "role1" {
			t.Error("unexpected role value")
		}

		if c.SessionDuration != credentials.SessionTokenDefaultDuration {
			t.Error("unexpected session duration")
		}

		if c.RoleDuration != credentials.AssumeRoleDefaultDuration {
			t.Error("unexpected role duration")
		}
	})

	t.Run("role non-default source", func(t *testing.T) {
		r, err := NewConfigResolver(nil)
		if err != nil {
			t.Error(err)
			return
		}

		c, err := r.ResolveConfig("role2")
		if err != nil {
			t.Error(err)
			return
		}

		if c.Region != "us-east-1" {
			t.Error("unexpected region")
		}

		if c.MfaSerial != "ABCDEFG" {
			t.Error("unexpected mfa serial value")
		}

		if c.RoleArn != "role2" {
			t.Error("unexpected role value")
		}

		if c.SessionDuration == 0 {
			t.Error("unexpected session duration")
		}

		if c.RoleDuration != credentials.AssumeRoleDefaultDuration {
			t.Error("unexpected role duration")
		}
	})

	t.Run("role bad source", func(t *testing.T) {
		r, err := NewConfigResolver(nil)
		if err != nil {
			t.Error(err)
			return
		}

		c, err := r.ResolveConfig("role3")
		if err != nil {
			t.Error(err)
			return
		}

		if c.SourceProfile != "other" {
			t.Error("source profile mismatch")
		}
	})

	t.Run("arn", func(t *testing.T) {
		os.Setenv(MfaSerialEnvVar, "654321")
		defer os.Unsetenv(MfaSerialEnvVar)

		arn := "arn:aws:iam::0123456789012:role/Admin"

		r, err := NewConfigResolver(nil)
		if err != nil {
			t.Error(err)
			return
		}

		c, err := r.ResolveConfig(arn)
		if err != nil {
			t.Error(err)
			return
		}

		if c.Region != "us-west-1" {
			t.Error("unexpected region")
		}

		if c.RoleArn != arn {
			t.Error("bad role value")
		}

		if c.MfaSerial != "654321" {
			t.Error("bad mfa value")
		}
	})

	t.Run("bad arn", func(t *testing.T) {

		arn := "arn:aws:iam::0123456789012:user/Admin"

		r, err := NewConfigResolver(nil)
		if err != nil {
			t.Error(err)
			return
		}

		_, err = r.ResolveConfig(arn)
		if err == nil {
			t.Error("did not receive expected error")
			return
		}
	})
}

func TestConfigResolver_ResolveDefaultConfig(t *testing.T) {
	os.Setenv(config.ConfigFileEnvVar, "test/config")
	defer os.Unsetenv(config.ConfigFileEnvVar)

	r, err := NewConfigResolver(nil)
	if err != nil {
		t.Error(err)
		return
	}

	t.Run("default", func(t *testing.T) {
		c, err := r.ResolveDefaultConfig()
		if err != nil {
			t.Error(err)
			return
		}

		if c.Region != "us-west-1" {
			t.Error("unexpected region")
		}

		if len(c.MfaSerial) > 0 {
			t.Error("mfa serial was set for default profile")
		}
	})

	t.Run("good env var", func(t *testing.T) {
		os.Setenv(DefaultProfileEnvVar, "source")
		defer os.Unsetenv(DefaultProfileEnvVar)

		c, err := r.ResolveDefaultConfig()
		if err != nil {
			t.Error(err)
			return
		}

		if c.Region != "us-east-1" {
			t.Error("unexpected region")
		}

		if len(c.MfaSerial) > 0 {
			t.Error("mfa serial was set for default profile")
		}

		if c.SessionDuration == 0 {
			t.Error("bad session duration")
		}
	})

	t.Run("bad env var", func(t *testing.T) {
		os.Setenv(DefaultProfileEnvVar, "bad")
		defer os.Unsetenv(DefaultProfileEnvVar)

		_, err := r.ResolveDefaultConfig()
		if err == nil {
			t.Error("did not receive expected error")
			return
		}
	})
}

func TestConfigResolver_ResolveProfileConfig(t *testing.T) {
	os.Setenv(config.ConfigFileEnvVar, "test/config")
	defer os.Unsetenv(config.ConfigFileEnvVar)

	r, err := NewConfigResolver(nil)
	if err != nil {
		t.Error(err)
		return
	}

	t.Run("profile exists", func(t *testing.T) {
		c, err := r.ResolveProfileConfig("role1")
		if err != nil {
			t.Error(err)
			return
		}

		if c.RoleArn != "role1" {
			t.Error("role mismatch")
		}
	})

	t.Run("empty profile", func(t *testing.T) {
		// Will look up default profile
		_, err := r.ResolveProfileConfig("")
		if err != nil {
			t.Error(err)
			return
		}
	})

	t.Run("bad profile", func(t *testing.T) {
		_, err := r.ResolveProfileConfig("bogus")
		if err == nil {
			t.Error("did not receive expected error")
			return
		}
	})
}

func TestConfigResolver_ResolveEnvConfig(t *testing.T) {
	os.Setenv(config.ConfigFileEnvVar, "../../.aws/config")
	env := make(map[string]string)
	env[RegionEnvVar] = "us-east-2"
	env[ProfileEnvVar] = "profile"
	env[SessionDurationEnvVar] = "1m"
	env[config.CredentialsFileEnvVar] = "test/config"

	for k, v := range env {
		os.Setenv(k, v)
		defer os.Unsetenv(k)
	}

	r, err := NewConfigResolver(nil)
	if err != nil {
		t.Error(err)
		return
	}

	t.Run("good", func(t *testing.T) {
		c, err := r.ResolveEnvConfig()
		if err != nil {
			t.Error(err)
			return
		}

		if c.Region != env[RegionEnvVar] {
			t.Error("bad region")
		}

		if c.SessionDuration != 1*time.Minute {
			t.Error("bad session duration")
		}

		if len(c.RoleArn) > 0 {
			t.Error("unexpected role arn")
		}
	})

	t.Run("bad session duration", func(t *testing.T) {
		os.Setenv(SessionDurationEnvVar, "ab")
		defer os.Unsetenv(SessionDurationEnvVar)

		_, err := r.ResolveEnvConfig()
		if err == nil {
			t.Error("did not see expected error")
			return
		}
	})

	t.Run("bad role duration", func(t *testing.T) {
		os.Setenv(RoleDurationEnvVar, "ab")
		defer os.Unsetenv(RoleDurationEnvVar)

		_, err := r.ResolveEnvConfig()
		if err == nil {
			t.Error("did not see expected error")
			return
		}
	})
}

func TestMergeConfig(t *testing.T) {
	t.Run("all nil", func(t *testing.T) {
		c := MergeConfig(nil)
		if c == nil {
			t.Error("nil config")
			return
		}

		if len(c.Region) > 0 {
			t.Error("unexpected region value")
		}

		if len(c.RoleArn) > 0 {
			t.Error("unexpected role arn")
		}

		if len(c.MfaSerial) > 0 {
			t.Error("unexpected mfa serial")
		}

		if len(c.ExternalID) > 0 {
			t.Error("unexpected external id")
		}

		if c.SessionDuration > 0 {
			t.Error("unexpeted session duration")
		}

		if c.RoleDuration > 0 {
			t.Error("unexpected role duration")
		}
	})

	t.Run("good", func(t *testing.T) {
		c := MergeConfig(
			nil,
			&AwsConfig{Region: "us-east-1"},
			&AwsConfig{MfaSerial: "123456"},
			nil,
			&AwsConfig{Region: "us-east-2", RoleArn: "my-role"})

		if c == nil {
			t.Error("unexpected nil config")
			return
		}

		if c.Region != "us-east-2" {
			t.Error("bad region")
		}

		if c.MfaSerial != "123456" {
			t.Error("bad mfa serial")
		}

		if c.RoleArn != "my-role" {
			t.Error("bad role")
		}
	})
}

func TestConfigResolver_WithLogger(t *testing.T) {
	r := new(configResolver).WithLogger(simple_logger.StdLogger)
	if r.log == nil {
		t.Error("unexpected nil logger")
	}
}

func ExampleDebugNilLogger() {
	r := new(configResolver)
	r.debug("test")
	// Output:
	//
}

func ExampleDebug() {
	l := simple_logger.NewLogger(os.Stdout, "", 0)
	l.SetLevel(simple_logger.DEBUG)
	r := new(configResolver).WithLogger(l)
	r.debug("test")
	// Output:
	// DEBUG test
}
<|endoftext|>


<commit_before>package handlers

import (
	"fmt"
	"net/http"
    "net/url"
    "io/ioutil"
    "strings"
    "github.com/nelsonleduc/calmanbot/handlers/models"
    "encoding/json"
    "bytes"
    "regexp"
)

type GMHook struct {}

func isValidHTTPURLString(s string)  bool {
    URL, _ := url.Parse(s)
    return (URL.Scheme == "http" || URL.Scheme == "https")
}

func HandleCalman(w http.ResponseWriter, r *http.Request) {

    message := ParseMessageJSON(r.Body)

    actions, _ := models.FetchActions(true)
    var act models.Action
    for _, a := range actions {
        match, err := regexp.MatchString(*a.Pattern, message.Text)
        fmt.Println(err)
        if match {
            act = a
            break
        }
    }

    updateAction(&act, message)

    bot, _ := models.FetchBot(message.GroupID)

    if act.IsURLType() {
        handleURLAction(act, w, bot)
    }
}

func handleURLAction(a models.Action, w http.ResponseWriter, b models.Bot) {

    fmt.Fprintln(w, a)
    resp, err := http.Get(a.Content)
    if err == nil {

        content, _ := ioutil.ReadAll(resp.Body)
        pathString := *a.DataPath

        str := ParseJSON(content, pathString)

        success := func(s string) {
            fmt.Printf("Success: %v\n", s)
            postText(b, s)
        }
        failure := func() {
            //Actually perform fallback here

            fmt.Printf("Failed")
        }

        if !validateURL(str, success) {
            fmt.Printf("Invalid URL: %v\n", str)

            oldStr := str
            for i := 0; i < 3 && oldStr == str; i++ {
                str = ParseJSON(content, pathString)
            }

            if !validateURL(str, success) {
                failure()
            }
        }
    }

    resp.Body.Close()
}

func postText(b models.Bot, t string) {

    postURL := "https://api.groupme.com/v3/bots/post"
    postBody := map[string]string {
        "bot_id": b.Key,
        "text": t,
    }

    encoded, _ := json.Marshal(postBody)

    http.Post(postURL, "application/json", bytes.NewReader(encoded))
}

func validateURL(u string, success func(string)) bool {

    client := http.Client{}
    if isValidHTTPURLString(u) {
        req, err := http.NewRequest("HEAD", u, nil)
        if err != nil {
            return false
        }

        resp, err := client.Do(req)

        if err == nil && resp.StatusCode >= 200 && resp.StatusCode < 300 {
            success(u)
        } else {
            return false
        }
    } else {
        success(u)
    }

    return true
}

func updateAction(a *models.Action, m models.Message) {
    text := url.QueryEscape(m.Text)

    a.Content = strings.Replace(a.Content, "{_text_}", text, -1)
}<commit_msg>Actually use the right part of a message<commit_after>package handlers

import (
	"fmt"
	"net/http"
    "net/url"
    "io/ioutil"
    "strings"
    "github.com/nelsonleduc/calmanbot/handlers/models"
    "encoding/json"
    "bytes"
    "regexp"
)

type GMHook struct {}

func isValidHTTPURLString(s string)  bool {
    URL, _ := url.Parse(s)
    return (URL.Scheme == "http" || URL.Scheme == "https")
}

func HandleCalman(w http.ResponseWriter, r *http.Request) {

    message := ParseMessageJSON(r.Body)
    bot, _ := models.FetchBot(message.GroupID)

    if !strings.HasPrefix(message.Text, "@" + bot.BotName) {
        return
    }

    actions, _ := models.FetchActions(true)
    var (
        act models.Action
        matched string
    )
    for _, a := range actions {
        r, err := regexp.Compile(*a.Pattern)
        matched = r.FindString(message.Text)
        fmt.Println(err)
        if matched != "" {
            act = a
            break
        }
    }

    updateAction(&act, matched)

    if act.IsURLType() {
        handleURLAction(act, w, bot)
    }
}

func handleURLAction(a models.Action, w http.ResponseWriter, b models.Bot) {

    fmt.Fprintln(w, a)
    resp, err := http.Get(a.Content)
    if err == nil {

        content, _ := ioutil.ReadAll(resp.Body)
        pathString := *a.DataPath

        str := ParseJSON(content, pathString)

        success := func(s string) {
            fmt.Printf("Success: %v\n", s)
            postText(b, s)
        }
        failure := func() {
            //Actually perform fallback here

            fmt.Printf("Failed")
        }

        if !validateURL(str, success) {
            fmt.Printf("Invalid URL: %v\n", str)

            oldStr := str
            for i := 0; i < 3 && oldStr == str; i++ {
                str = ParseJSON(content, pathString)
            }

            if !validateURL(str, success) {
                failure()
            }
        }
    }

    resp.Body.Close()
}

func postText(b models.Bot, t string) {

    postURL := "https://api.groupme.com/v3/bots/post"
    postBody := map[string]string {
        "bot_id": b.Key,
        "text": t,
    }

    encoded, _ := json.Marshal(postBody)

    http.Post(postURL, "application/json", bytes.NewReader(encoded))
}

func validateURL(u string, success func(string)) bool {

    client := http.Client{}
    if isValidHTTPURLString(u) {
        req, err := http.NewRequest("HEAD", u, nil)
        if err != nil {
            return false
        }

        resp, err := client.Do(req)

        if err == nil && resp.StatusCode >= 200 && resp.StatusCode < 300 {
            success(u)
        } else {
            return false
        }
    } else {
        success(u)
    }

    return true
}

func updateAction(a *models.Action, text string) {
    text = url.QueryEscape(text)

    a.Content = strings.Replace(a.Content, "{_text_}", text, -1)
}<|endoftext|>


<commit_before>package handlers

import (
	"encoding/json"
	"fmt"
	"log"
	"time"

	"github.com/dgraph-io/badger/v2"
	"github.com/dustin/go-humanize"
	"github.com/gojp/goreportcard/check"
	"github.com/gojp/goreportcard/download"
)

type notFoundError struct {
	repo string
}

func (n notFoundError) Error() string {
	return fmt.Sprintf("%q not found in cache", n.repo)
}

func dirName(repo string) string {
	return fmt.Sprintf("_repos/src/%s", repo)
}

func getFromCache(db *badger.DB, repo string) (checksResp, error) {
	// try and fetch from badger
	resp := checksResp{}
	err := db.View(func(txn *badger.Txn) error {
		item, err := txn.Get([]byte(RepoPrefix + repo))
		if err != nil && err != badger.ErrKeyNotFound {
			return err
		}

		if item != nil {
			log.Printf("repo %q found in badger cache", repo)
			err = item.Value(func(val []byte) error {
				err = json.Unmarshal(val, &resp)
				if err != nil {
					return fmt.Errorf("failed to parse JSON for %q in cache", repo)
				}

				return nil
			})
		}

		if item == nil {
			log.Println("Repo not found in badger")
			return notFoundError{repo}
		}

		return nil
	})

	if err != nil {
		return resp, err
	}

	resp.LastRefresh = resp.LastRefresh.UTC()
	resp.LastRefreshFormatted = resp.LastRefresh.Format(time.UnixDate)
	resp.LastRefreshHumanized = humanize.Time(resp.LastRefresh.UTC())

	return resp, nil
}

type checksResp struct {
	Checks               []check.Score `json:"checks"`
	Average              float64       `json:"average"`
	Grade                check.Grade   `json:"grade"`
	Files                int           `json:"files"`
	Issues               int           `json:"issues"`
	Repo                 string        `json:"repo"`
	ResolvedRepo         string        `json:"resolvedRepo"`
	LastRefresh          time.Time     `json:"last_refresh"`
	LastRefreshFormatted string        `json:"formatted_last_refresh"`
	LastRefreshHumanized string        `json:"humanized_last_refresh"`
}

func newChecksResp(db *badger.DB, repo string, forceRefresh bool) (checksResp, error) {
	if !forceRefresh {
		resp, err := getFromCache(db, repo)
		if err != nil {
			// just log the error and continue
			log.Println(err)
		} else {
			resp.Grade = check.GradeFromPercentage(resp.Average * 100) // grade is not stored for some repos, yet
			return resp, nil
		}
	}

	// fetch the repo and grade it
	repoRoot, err := download.Download(repo, "_repos/src")
	if err != nil {
		return checksResp{}, fmt.Errorf("could not clone repo: %v", err)
	}

	repo = repoRoot.Root
	checkResult, err := check.Run(dirName(repo))
	if err != nil {
		return checksResp{}, err
	}

	t := time.Now().UTC()
	resp := checksResp{
		Checks:               checkResult.Checks,
		Average:              checkResult.Average,
		Grade:                checkResult.Grade,
		Files:                checkResult.Files,
		Issues:               checkResult.Issues,
		Repo:                 repo,
		ResolvedRepo:         repoRoot.Repo,
		LastRefresh:          t,
		LastRefreshFormatted: t.Format(time.UnixDate),
		LastRefreshHumanized: humanize.Time(t),
	}

	respBytes, err := json.Marshal(resp)
	if err != nil {
		return checksResp{}, fmt.Errorf("could not marshal json: %v", err)
	}

	// is this a new repo? if so, increase the count in the high scores bucket later
	isNewRepo := false
	var oldRepoBytes []byte
	err = db.View(func(txn *badger.Txn) error {
		item, err := txn.Get([]byte(RepoPrefix + repo))
		if err != nil {
			return err
		}

		err = item.Value(func(val []byte) error {
			oldRepoBytes = val

			return nil
		})

		return err
	})

	if err != nil && err != badger.ErrKeyNotFound {
		log.Println("ERROR getting repo badger:", err)
	}

	isNewRepo = oldRepoBytes == nil

	// if this is a new repo, or the user force-refreshed, update the cache
	if isNewRepo || forceRefresh {
		err = db.Update(func(txn *badger.Txn) error {
			log.Printf("Saving repo %q to cache...", repo)

			// save repo to cache
			err = txn.Set([]byte(RepoPrefix+repo), respBytes)
			if err != nil {
				return err
			}

			return updateMetadata(txn, resp, repo, isNewRepo)
		})

		if err != nil {
			log.Println("Badger writing error:", err)
		}

	}

	err = db.Update(func(txn *badger.Txn) error {
		return updateRecentlyViewed(txn, repo)
	})

	if err != nil {
		log.Printf("ERROR: could not update recently viewed: %v", err)
	}

	return resp, nil
}
<commit_msg>update log lines<commit_after>package handlers

import (
	"encoding/json"
	"fmt"
	"log"
	"time"

	"github.com/dgraph-io/badger/v2"
	"github.com/dustin/go-humanize"
	"github.com/gojp/goreportcard/check"
	"github.com/gojp/goreportcard/download"
)

type notFoundError struct {
	repo string
}

func (n notFoundError) Error() string {
	return fmt.Sprintf("%q not found in cache", n.repo)
}

func dirName(repo string) string {
	return fmt.Sprintf("_repos/src/%s", repo)
}

func getFromCache(db *badger.DB, repo string) (checksResp, error) {
	// try and fetch from badger
	resp := checksResp{}
	err := db.View(func(txn *badger.Txn) error {
		item, err := txn.Get([]byte(RepoPrefix + repo))
		if err != nil && err != badger.ErrKeyNotFound {
			return err
		}

		if item != nil {
			err = item.Value(func(val []byte) error {
				err = json.Unmarshal(val, &resp)
				if err != nil {
					return fmt.Errorf("failed to parse JSON for %q in cache", repo)
				}

				return nil
			})
		}

		if item == nil {
			log.Println("Repo %q not found in badger cache", repo)
			return notFoundError{repo}
		}

		return nil
	})

	if err != nil {
		return resp, err
	}

	resp.LastRefresh = resp.LastRefresh.UTC()
	resp.LastRefreshFormatted = resp.LastRefresh.Format(time.UnixDate)
	resp.LastRefreshHumanized = humanize.Time(resp.LastRefresh.UTC())

	return resp, nil
}

type checksResp struct {
	Checks               []check.Score `json:"checks"`
	Average              float64       `json:"average"`
	Grade                check.Grade   `json:"grade"`
	Files                int           `json:"files"`
	Issues               int           `json:"issues"`
	Repo                 string        `json:"repo"`
	ResolvedRepo         string        `json:"resolvedRepo"`
	LastRefresh          time.Time     `json:"last_refresh"`
	LastRefreshFormatted string        `json:"formatted_last_refresh"`
	LastRefreshHumanized string        `json:"humanized_last_refresh"`
}

func newChecksResp(db *badger.DB, repo string, forceRefresh bool) (checksResp, error) {
	if !forceRefresh {
		resp, err := getFromCache(db, repo)
		if err != nil {
			// just log the error and continue
			log.Println(err)
		} else {
			resp.Grade = check.GradeFromPercentage(resp.Average * 100) // grade is not stored for some repos, yet
			return resp, nil
		}
	}

	// fetch the repo and grade it
	repoRoot, err := download.Download(repo, "_repos/src")
	if err != nil {
		return checksResp{}, fmt.Errorf("could not clone repo: %v", err)
	}

	repo = repoRoot.Root
	checkResult, err := check.Run(dirName(repo))
	if err != nil {
		return checksResp{}, err
	}

	t := time.Now().UTC()
	resp := checksResp{
		Checks:               checkResult.Checks,
		Average:              checkResult.Average,
		Grade:                checkResult.Grade,
		Files:                checkResult.Files,
		Issues:               checkResult.Issues,
		Repo:                 repo,
		ResolvedRepo:         repoRoot.Repo,
		LastRefresh:          t,
		LastRefreshFormatted: t.Format(time.UnixDate),
		LastRefreshHumanized: humanize.Time(t),
	}

	respBytes, err := json.Marshal(resp)
	if err != nil {
		return checksResp{}, fmt.Errorf("could not marshal json: %v", err)
	}

	// is this a new repo? if so, increase the count in the high scores bucket later
	isNewRepo := false
	var oldRepoBytes []byte
	err = db.View(func(txn *badger.Txn) error {
		item, err := txn.Get([]byte(RepoPrefix + repo))
		if err != nil {
			return err
		}

		err = item.Value(func(val []byte) error {
			oldRepoBytes = val

			return nil
		})

		return err
	})

	if err != nil && err != badger.ErrKeyNotFound {
		log.Println("ERROR getting repo badger:", err)
	}

	isNewRepo = oldRepoBytes == nil

	// if this is a new repo, or the user force-refreshed, update the cache
	if isNewRepo || forceRefresh {
		err = db.Update(func(txn *badger.Txn) error {
			log.Printf("Saving repo %q to cache...", repo)

			// save repo to cache
			err = txn.Set([]byte(RepoPrefix+repo), respBytes)
			if err != nil {
				return err
			}

			return updateMetadata(txn, resp, repo, isNewRepo)
		})

		if err != nil {
			log.Println("Badger writing error:", err)
		}

	}

	err = db.Update(func(txn *badger.Txn) error {
		return updateRecentlyViewed(txn, repo)
	})

	if err != nil {
		log.Printf("ERROR: could not update recently viewed: %v", err)
	}

	return resp, nil
}
<|endoftext|>


<commit_before>package v7

import (
	"encoding/json"

	"code.cloudfoundry.org/cli/util/ui"

	"code.cloudfoundry.org/cli/actor/sharedaction"
	"code.cloudfoundry.org/cli/actor/v7action"
	"code.cloudfoundry.org/cli/command"
	"code.cloudfoundry.org/cli/command/flag"
	"code.cloudfoundry.org/cli/command/v7/shared"
	"code.cloudfoundry.org/clock"
)

//go:generate counterfeiter . SecurityGroupActor

type SecurityGroupActor interface {
	GetSecurityGroup(securityGroupName string) (v7action.SecurityGroupSummary, v7action.Warnings, error)
}

type SecurityGroupCommand struct {
	RequiredArgs    flag.SecurityGroup `positional-args:"yes"`
	usage           interface{}        `usage:"CF_NAME security-group SECURITY_GROUP"`
	relatedCommands interface{}        `related_commands:"bind-running-security-group, bind-security-group, bind-staging-security-group"`

	UI          command.UI
	Config      command.Config
	SharedActor command.SharedActor
	Actor       SecurityGroupActor
}

func (cmd *SecurityGroupCommand) Setup(config command.Config, ui command.UI) error {
	cmd.UI = ui
	cmd.Config = config
	sharedActor := sharedaction.NewActor(config)
	cmd.SharedActor = sharedActor

	ccClient, uaaClient, err := shared.GetNewClientsAndConnectToCF(config, ui, "")
	if err != nil {
		return err
	}
	cmd.Actor = v7action.NewActor(ccClient, config, sharedActor, uaaClient, clock.NewClock())

	return nil
}

func (cmd SecurityGroupCommand) Execute(args []string) error {
	err := cmd.SharedActor.CheckTarget(false, false)
	if err != nil {
		return err
	}

	user, err := cmd.Config.CurrentUser()
	if err != nil {
		return err
	}

	cmd.UI.DisplayTextWithFlavor("Getting info for security group {{.GroupName}} as {{.Username}}...", map[string]interface{}{
		"GroupName": cmd.RequiredArgs.SecurityGroup,
		"Username":  user.Name,
	})
	cmd.UI.DisplayNewline()

	securityGroupSummary, warnings, err := cmd.Actor.GetSecurityGroup(cmd.RequiredArgs.SecurityGroup)
	cmd.UI.DisplayWarnings(warnings)
	if err != nil {
		return err
	}

	cmd.UI.DisplayKeyValueTable("", [][]string{
		{cmd.UI.TranslateText("name:"), securityGroupSummary.Name},
		{cmd.UI.TranslateText("rules:"), ""},
	}, 3)

	jsonRules, err := json.MarshalIndent(securityGroupSummary.Rules, "\t", "\t")
	if err != nil {
		return err
	}

	cmd.UI.DisplayText(string(jsonRules))

	cmd.UI.DisplayNewline()

	if len(securityGroupSummary.SecurityGroupSpaces) > 0 {
		table := [][]string{{"organization", "space"}}
		for _, securityGroupSpace := range securityGroupSummary.SecurityGroupSpaces {
			table = append(table, []string{securityGroupSpace.OrgName, securityGroupSpace.SpaceName})
		}
		cmd.UI.DisplayTableWithHeader("", table, ui.DefaultTableSpacePadding)
	} else {
		cmd.UI.DisplayText("No spaces assigned")
	}

	return nil
}
<commit_msg>Style change for **V7** security group command<commit_after>package v7

import (
	"encoding/json"

	"code.cloudfoundry.org/cli/util/ui"

	"code.cloudfoundry.org/cli/actor/sharedaction"
	"code.cloudfoundry.org/cli/actor/v7action"
	"code.cloudfoundry.org/cli/command"
	"code.cloudfoundry.org/cli/command/flag"
	"code.cloudfoundry.org/cli/command/v7/shared"
	"code.cloudfoundry.org/clock"
)

//go:generate counterfeiter . SecurityGroupActor

type SecurityGroupActor interface {
	GetSecurityGroup(securityGroupName string) (v7action.SecurityGroupSummary, v7action.Warnings, error)
}

type SecurityGroupCommand struct {
	RequiredArgs    flag.SecurityGroup `positional-args:"yes"`
	usage           interface{}        `usage:"CF_NAME security-group SECURITY_GROUP"`
	relatedCommands interface{}        `related_commands:"bind-running-security-group, bind-security-group, bind-staging-security-group"`

	UI          command.UI
	Config      command.Config
	SharedActor command.SharedActor
	Actor       SecurityGroupActor
}

func (cmd *SecurityGroupCommand) Setup(config command.Config, ui command.UI) error {
	cmd.UI = ui
	cmd.Config = config
	sharedActor := sharedaction.NewActor(config)
	cmd.SharedActor = sharedActor

	ccClient, uaaClient, err := shared.GetNewClientsAndConnectToCF(config, ui, "")
	if err != nil {
		return err
	}
	cmd.Actor = v7action.NewActor(ccClient, config, sharedActor, uaaClient, clock.NewClock())

	return nil
}

func (cmd SecurityGroupCommand) Execute(args []string) error {
	err := cmd.SharedActor.CheckTarget(false, false)
	if err != nil {
		return err
	}

	user, err := cmd.Config.CurrentUser()
	if err != nil {
		return err
	}

	cmd.UI.DisplayTextWithFlavor("Getting info for security group {{.GroupName}} as {{.Username}}...", map[string]interface{}{
		"GroupName": cmd.RequiredArgs.SecurityGroup,
		"Username":  user.Name,
	})
	cmd.UI.DisplayNewline()

	securityGroupSummary, warnings, err := cmd.Actor.GetSecurityGroup(cmd.RequiredArgs.SecurityGroup)
	cmd.UI.DisplayWarnings(warnings)
	if err != nil {
		return err
	}

	cmd.UI.DisplayKeyValueTable("", [][]string{
		{cmd.UI.TranslateText("name:"), securityGroupSummary.Name},
		{cmd.UI.TranslateText("rules:"), ""},
	}, 3)

	jsonRules, err := json.MarshalIndent(securityGroupSummary.Rules, "\t", "\t")
	if err != nil {
		return err
	}

	cmd.UI.DisplayText("\t" + string(jsonRules))

	cmd.UI.DisplayNewline()

	if len(securityGroupSummary.SecurityGroupSpaces) > 0 {
		table := [][]string{{"organization", "space"}}
		for _, securityGroupSpace := range securityGroupSummary.SecurityGroupSpaces {
			table = append(table, []string{securityGroupSpace.OrgName, securityGroupSpace.SpaceName})
		}
		cmd.UI.DisplayTableWithHeader("", table, ui.DefaultTableSpacePadding)
	} else {
		cmd.UI.DisplayText("No spaces assigned")
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import "testing"

func TestIconsFind_countAll(t *testing.T) {
	fi := findIcons([]string{""})

	ex := 519
	ac := len(fi)
	if ex != ac {
		t.Errorf("failed to find icon: expected %v, got %v", ex, ac)
	}
}

func TestIconsFind_countZero(t *testing.T) {
	fi := findIcons([]string{"foo-bar-baz"})

	ex := 0
	ac := len(fi)
	if ex != ac {
		t.Errorf("failed to find icon: expected %v, got %v", ex, ac)
	}
}

func TestIconsFind_firstID(t *testing.T) {
	fi := findIcons([]string{""})

	ex := "adjust"
	ac := fi[0].ID
	if ex != ac {
		t.Errorf("failed to find icon: expected %v, got %v", ex, ac)
	}
}

func TestIconsFind_lastID(t *testing.T) {
	fi := findIcons([]string{""})

	ex := "youtube-square"
	ac := fi[len(fi)-1].ID
	if ex != ac {
		t.Errorf("failed to find icon: expected %v, got %v", ex, ac)
	}
}

func findIcons(terms []string) Icons {
	return NewIcons().Find(terms)
}
<commit_msg>Add test: TestIconsFind_countOne<commit_after>package main

import "testing"

func TestIconsFind_countAll(t *testing.T) {
	fi := findIcons([]string{""})

	ex := 519
	ac := len(fi)
	if ex != ac {
		t.Errorf("failed to find icon: expected %v, got %v", ex, ac)
	}
}

func TestIconsFind_countZero(t *testing.T) {
	fi := findIcons([]string{"foo-bar-baz"})

	ex := 0
	ac := len(fi)
	if ex != ac {
		t.Errorf("failed to find icon: expected %v, got %v", ex, ac)
	}
}

func TestIconsFind_countOne(t *testing.T) {
	fi := findIcons([]string{"github-square"})

	ex := 1
	ac := len(fi)
	if ex != ac {
		t.Errorf("failed to find icon: expected %v, got %v", ex, ac)
	}
}

func TestIconsFind_firstID(t *testing.T) {
	fi := findIcons([]string{""})

	ex := "adjust"
	ac := fi[0].ID
	if ex != ac {
		t.Errorf("failed to find icon: expected %v, got %v", ex, ac)
	}
}

func TestIconsFind_lastID(t *testing.T) {
	fi := findIcons([]string{""})

	ex := "youtube-square"
	ac := fi[len(fi)-1].ID
	if ex != ac {
		t.Errorf("failed to find icon: expected %v, got %v", ex, ac)
	}
}

func findIcons(terms []string) Icons {
	return NewIcons().Find(terms)
}
<|endoftext|>


<commit_before>// 15 april 2015
package main

import (
	"fmt"
	"os"
	"github.com/andlabs/pgidl"
)

var pkgtypes = map[string]string{}

func typedecl(t *pgidl.Type, name string) string {
	if t == nil {
		return "void " + name
	}
	if t.IsFuncPtr {
		return cfuncptrdecl(t.FuncType, name)
	}
	s := t.Name + " "
	if pkgtypes[t.Name] != "" {
		s = pkgtypes[t.Name] + " "
	}
	for i := uint(0); i < t.NumPtrs; i++ {
		s += "*"
	}
	return s + name
}

func arglist(a []*pgidl.Arg) string {
	if len(a) == 0 {
		return "void"
	}
	s := typedecl(a[0].Type, a[0].Name)
	for i := 1; i < len(a); i++ {
		s += ", " + typedecl(a[i].Type, a[i].Name)
	}
	return s
}

func cfuncdecl(f *pgidl.Func, name string) string {
	fd := name + "(" + arglist(f.Args) + ")"
	return "extern " + typedecl(f.Ret, fd) + ";"
}

func cfuncptrdecl(f *pgidl.Func, name string) string {
	name = "(*" + name + ")"
	fd := name + "(" + arglist(f.Args) + ")"
	return typedecl(f.Ret, fd)
}

func cmethodmacro(f *pgidl.Func, typename string) string {
	s := "#define " + typename + f.Name + "("
	first := true
	for _, a := range f.Args {
		if !first {
			s += ", "
		}
		s += a.Name
		first = false
	}
	s += ") ("
	s += "(*((this)->" + f.Name + "))"
	s += "("
	first = true
	for _, a := range f.Args {
		if !first {
			s += ", "
		}
		s += "(" + a.Name + ")"
		first = false
	}
	s += ")"
	s += ")"
	return s
}

func genpkgfunc(f *pgidl.Func, prefix string) {
	fmt.Printf("%s\n", cfuncdecl(f, prefix + f.Name))
}

func genstruct(s *pgidl.Struct, prefix string) {
	fmt.Printf("struct %s%s {\n", prefix, s.Name)
	for _, f := range s.Fields {
		fmt.Printf("\t%s;\n", typedecl(f.Type, f.Name))
	}
	fmt.Printf("};\n")
}

func geniface(i *pgidl.Interface, prefix string) {
	fmt.Printf("struct %s%s {\n", prefix, i.Name)
	if i.From != "" {
		fmt.Printf("\t%s%s base;\n", prefix, i.From)
	}
	for _, f := range i.Fields {
		fmt.Printf("\t%s;\n", typedecl(f.Type, f.Name))
	}
	for _, m := range i.Methods {
		// hack our this pointer in
		m.Args = append([]*pgidl.Arg{
			&pgidl.Arg{
				Name:	"this",
				Type:	&pgidl.Type{
					Name:	prefix + i.Name,
					NumPtrs:	1,
				},
			},
		}, m.Args...)
		fmt.Printf("\t%s;\n", cfuncptrdecl(m, m.Name))
		fmt.Printf("%s\n", cmethodmacro(m, prefix + i.Name))
	}
	fmt.Printf("};\n")
	fmt.Printf("#define %s%s(this) ((%s%s *) (this))\n",
		prefix, i.Name,
		prefix, i.Name)
}

func genenum(e *pgidl.Enum, prefix string) {
	fmt.Printf("enum %s%s {\n", prefix, e.Name)
	for _, m := range e.Members {
		fmt.Printf("\t%s%s%s,\n", prefix, e.Name, m)
	}
	fmt.Printf("};\n")
}

func genpkg(p *pgidl.Package) {
	for _, s := range p.Structs {
		fmt.Printf("typedef struct %s%s %s%s;\n",
			p.Name, s.Name,
			p.Name, s.Name)
		pkgtypes[s.Name] = p.Name + s.Name
	}
	for _, i := range p.Interfaces {
		fmt.Printf("typedef struct %s%s %s%s;\n",
			p.Name, i.Name,
			p.Name, i.Name)
		pkgtypes[i.Name] = p.Name + i.Name
	}
	for _, e := range p.Enums {
		fmt.Printf("typedef enum %s%s %s%s;\n",
			p.Name, e.Name,
			p.Name, e.Name)
	}
	for _, o := range p.Order {
		switch o.Which {
		case pgidl.Funcs:
			genpkgfunc(p.Funcs[o.Index], p.Name)
		case pgidl.Structs:
			genstruct(p.Structs[o.Index], p.Name)
		case pgidl.Interfaces:
			geniface(p.Interfaces[o.Index], p.Name)
		case pgidl.Raws:
			fmt.Printf("%s\n", p.Raws[o.Index])
		case pgidl.Enums:
			genenum(p.Enums[o.Index], p.Name)
		}
	}
}

func main() {
	idl, errs := pgidl.Parse(os.Stdin, "<stdin>")
	if len(errs) != 0 {
		for _, e := range errs {
			fmt.Fprintf(os.Stderr, "%s\n", e)
		}
		os.Exit(1)
	}
	fmt.Printf("// generated by idl2h; do not edit\n")
	for _, p := range idl {
		genpkg(p)
	}
}
<commit_msg>Forgot to specify enum types names as such in idl2h.<commit_after>// 15 april 2015
package main

import (
	"fmt"
	"os"
	"github.com/andlabs/pgidl"
)

var pkgtypes = map[string]string{}

func typedecl(t *pgidl.Type, name string) string {
	if t == nil {
		return "void " + name
	}
	if t.IsFuncPtr {
		return cfuncptrdecl(t.FuncType, name)
	}
	s := t.Name + " "
	if pkgtypes[t.Name] != "" {
		s = pkgtypes[t.Name] + " "
	}
	for i := uint(0); i < t.NumPtrs; i++ {
		s += "*"
	}
	return s + name
}

func arglist(a []*pgidl.Arg) string {
	if len(a) == 0 {
		return "void"
	}
	s := typedecl(a[0].Type, a[0].Name)
	for i := 1; i < len(a); i++ {
		s += ", " + typedecl(a[i].Type, a[i].Name)
	}
	return s
}

func cfuncdecl(f *pgidl.Func, name string) string {
	fd := name + "(" + arglist(f.Args) + ")"
	return "extern " + typedecl(f.Ret, fd) + ";"
}

func cfuncptrdecl(f *pgidl.Func, name string) string {
	name = "(*" + name + ")"
	fd := name + "(" + arglist(f.Args) + ")"
	return typedecl(f.Ret, fd)
}

func cmethodmacro(f *pgidl.Func, typename string) string {
	s := "#define " + typename + f.Name + "("
	first := true
	for _, a := range f.Args {
		if !first {
			s += ", "
		}
		s += a.Name
		first = false
	}
	s += ") ("
	s += "(*((this)->" + f.Name + "))"
	s += "("
	first = true
	for _, a := range f.Args {
		if !first {
			s += ", "
		}
		s += "(" + a.Name + ")"
		first = false
	}
	s += ")"
	s += ")"
	return s
}

func genpkgfunc(f *pgidl.Func, prefix string) {
	fmt.Printf("%s\n", cfuncdecl(f, prefix + f.Name))
}

func genstruct(s *pgidl.Struct, prefix string) {
	fmt.Printf("struct %s%s {\n", prefix, s.Name)
	for _, f := range s.Fields {
		fmt.Printf("\t%s;\n", typedecl(f.Type, f.Name))
	}
	fmt.Printf("};\n")
}

func geniface(i *pgidl.Interface, prefix string) {
	fmt.Printf("struct %s%s {\n", prefix, i.Name)
	if i.From != "" {
		fmt.Printf("\t%s%s base;\n", prefix, i.From)
	}
	for _, f := range i.Fields {
		fmt.Printf("\t%s;\n", typedecl(f.Type, f.Name))
	}
	for _, m := range i.Methods {
		// hack our this pointer in
		m.Args = append([]*pgidl.Arg{
			&pgidl.Arg{
				Name:	"this",
				Type:	&pgidl.Type{
					Name:	prefix + i.Name,
					NumPtrs:	1,
				},
			},
		}, m.Args...)
		fmt.Printf("\t%s;\n", cfuncptrdecl(m, m.Name))
		fmt.Printf("%s\n", cmethodmacro(m, prefix + i.Name))
	}
	fmt.Printf("};\n")
	fmt.Printf("#define %s%s(this) ((%s%s *) (this))\n",
		prefix, i.Name,
		prefix, i.Name)
}

func genenum(e *pgidl.Enum, prefix string) {
	fmt.Printf("enum %s%s {\n", prefix, e.Name)
	for _, m := range e.Members {
		fmt.Printf("\t%s%s%s,\n", prefix, e.Name, m)
	}
	fmt.Printf("};\n")
}

func genpkg(p *pgidl.Package) {
	for _, s := range p.Structs {
		fmt.Printf("typedef struct %s%s %s%s;\n",
			p.Name, s.Name,
			p.Name, s.Name)
		pkgtypes[s.Name] = p.Name + s.Name
	}
	for _, i := range p.Interfaces {
		fmt.Printf("typedef struct %s%s %s%s;\n",
			p.Name, i.Name,
			p.Name, i.Name)
		pkgtypes[i.Name] = p.Name + i.Name
	}
	for _, e := range p.Enums {
		fmt.Printf("typedef enum %s%s %s%s;\n",
			p.Name, e.Name,
			p.Name, e.Name)
		pkgtypes[e.Name] = p.Name + e.Name
	}
	for _, o := range p.Order {
		switch o.Which {
		case pgidl.Funcs:
			genpkgfunc(p.Funcs[o.Index], p.Name)
		case pgidl.Structs:
			genstruct(p.Structs[o.Index], p.Name)
		case pgidl.Interfaces:
			geniface(p.Interfaces[o.Index], p.Name)
		case pgidl.Raws:
			fmt.Printf("%s\n", p.Raws[o.Index])
		case pgidl.Enums:
			genenum(p.Enums[o.Index], p.Name)
		}
	}
}

func main() {
	idl, errs := pgidl.Parse(os.Stdin, "<stdin>")
	if len(errs) != 0 {
		for _, e := range errs {
			fmt.Fprintf(os.Stderr, "%s\n", e)
		}
		os.Exit(1)
	}
	fmt.Printf("// generated by idl2h; do not edit\n")
	for _, p := range idl {
		genpkg(p)
	}
}
<|endoftext|>


<commit_before>package math

import (
	"errors"
  "sort"
)

// A translation of Apache Commons Math.
// Returns an interpolation function, valid function, and an error if any.
func BicubicInterpolator(
	xval, yval []float64,
	fval [][]float64) (
	func(float64, float64) (float64, error),
	func(float64, float64) bool,
	error) {

	if len(xval) == 0 || len(yval) == 0 || len(fval) == 0 {
		return nil, nil, errors.New("An array is zero length")
	}

	// Check input order.
	for i := 1; i < len(xval); i++ {
		if xval[i] <= xval[i-1] {
			return nil, nil, errors.New("X values are not monotonically increasing.")
		}
	}

	for i := 1; i < len(yval); i++ {
		if yval[i] <= yval[i-1] {
			return nil, nil, errors.New("Y values are not monotonically increasing.")
		}
	}

	dFdX := make([][]float64, len(xval))
	dFdY := make([][]float64, len(xval))
	d2FdXdY := make([][]float64, len(xval))
	for i := 0; i < len(xval); i++ {
		dFdX[i] = make([]float64, len(yval))
		dFdY[i] = make([]float64, len(yval))
		d2FdXdY[i] = make([]float64, len(yval))
	}

	for i := 1; i < len(xval)-1; i++ {
		nI := i + 1
		pI := i - 1

		nX := xval[nI]
		pX := xval[pI]

		deltaX := nX - pX

		for j := 1; j < len(yval)-1; j++ {
			nJ := j + 1
			pJ := j - 1

			nY := yval[nJ]
			pY := yval[pJ]

			deltaY := nY - pY

			dFdX[i][j] = (fval[nI][j] - fval[pI][j]) / deltaX
			dFdY[i][j] = (fval[i][nJ] - fval[i][pJ]) / deltaY

			deltaXY := deltaX * deltaY

			d2FdXdY[j][j] = (fval[nI][nJ] - fval[nI][pJ] - fval[pI][nJ] + fval[pI][pJ]) / deltaXY
		}
	}

	isValidFn := func(x, y float64) bool {
		if x < xval[1] || x > xval[len(xval)-2] || y < yval[1] || y > yval[len(yval)-2] {
			return false
		} else {
			return true
		}
	}

	interpolateFn, err := bicubicInterplationFunction(xval, yval, fval, dFdX, dFdY, d2FdXdY)
	if err != nil {
		return nil, nil, err
	}

	return interpolateFn, isValidFn, nil
}

func bicubicInterplationFunction(
	xval, yval []float64,
	f, dFdX, dFdY, d2FdXdY [][]float64) (func(x, y float64) (float64, error), error) {

	if len(xval) == 0 || len(yval) == 0 || len(f) == 0 || len(f[0]) == 0 {
		return nil, errors.New("Input length is zero.")
	}
	if len(xval) != len(f) {
		return nil, errors.New("X len does not equal f len.")
	}
	if len(xval) != len(dFdX) {
		return nil, errors.New("Dimensions don't match between x and dFDx.")
	}
	if len(xval) != len(dFdY) {
		return nil, errors.New("Dimensions don't match between x and dFDY.")
	}
	if len(xval) != len(d2FdXdY) {
		return nil, errors.New("Dimensions don't match between x and d2FdXdY.")
	}

	// We skip x and y order checks because it is done in the calling function.

	lastI := len(xval) - 1
	lastJ := len(yval) - 1
	splines := make([][]func(float64, float64) (float64, error), lastI)

	for i := 0; i < lastI; i++ {
		splines[i] = make([]func(float64, float64) (float64, error), lastJ)
		if len(f[i]) != len(yval) {
			return nil, errors.New("Dimension mismatch of f.")
		}
		if len(dFdX[i]) != len(yval) {
			return nil, errors.New("Dimension mismatch of dFdX.")
		}
		if len(dFdY[i]) != len(yval) {
			return nil, errors.New("Dimension mismatch of dFdY.")
		}
		if len(d2FdXdY[i]) != len(yval) {
			return nil, errors.New("Dimension mismatch of d2FdXdY[i].")
		}
		ip1 := i + 1
		xR := xval[ip1] - xval[i]
		for j := 0; j < lastJ; j++ {
			jp1 := j + 1
			yR := yval[jp1] - yval[j]
			xRyR := xR * yR
			beta := []float64{
				f[i][j], f[ip1][j], f[i][jp1], f[ip1][jp1],
				dFdX[i][j] * xR, dFdX[ip1][j] * xR, dFdX[i][jp1] * xR, dFdX[ip1][jp1] * xR,
				dFdY[i][j] * yR, dFdY[ip1][j] * yR, dFdY[i][jp1] * yR, dFdY[ip1][jp1] * yR,
				d2FdXdY[i][j] * xRyR, d2FdXdY[ip1][j] * xRyR, d2FdXdY[i][jp1] * xRyR, d2FdXdY[ip1][jp1] * xRyR}

			splines[i][j] = bicubicFunction(computeSplineCoefficients(beta))
		}
	}

  interpF := func(x, y float64)(float64, error) {
    i := searchIndex(x, xval)
    j := searchIndex(y, yval)

    xN := (x - xval[i]) / (xval[i+1] - xval[i])
    yN := (y - yval[j]) / (yval[j+1] - yval[j])

    return splines[i][j](xN, yN)
  }

	return interpF, nil
}

func computeSplineCoefficients(beta []float64) []float64 {
	NUM_COEFF := 16
	AINV := [][]float64{
		{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{-3, 3, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{2, -2, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, -2, -1, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 1, 1, 0, 0},
		{-3, 0, 3, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, -3, 0, 3, 0, 0, 0, 0, 0, -2, 0, -1, 0},
		{9, -9, -9, 9, 6, 3, -6, -3, 6, -6, 3, -3, 4, 2, 2, 1},
		{-6, 6, 6, -6, -3, -3, 3, 3, -4, 4, -2, 2, -2, -2, -1, -1},
		{2, 0, -2, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0, 0, 1, 0, 1, 0},
		{-6, 6, 6, -6, -4, -2, 4, 2, -3, 3, -3, 3, -2, -1, -2, -1},
		{4, -4, -4, 4, 2, 2, -2, -2, 2, -2, 2, -2, 1, 1, 1, 1}}

	a := make([]float64, NUM_COEFF)
	for i := 0; i < NUM_COEFF; i++ {
		result := float64(0)
		row := AINV[i]
		for j := 0; j < NUM_COEFF; j++ {
			result +=  (row[j] * beta[j])
		}
		a[i] = result
	}
	return a
}

func bicubicFunction(coeff []float64) func(float64, float64) (float64, error) {

	N := 4

	a := make([][]float64, N)
	for j := range a {
		a[j] = make([]float64, N)
		aJ := a[j]
		for i := range aJ {
			aJ[i] = coeff[i*N+j]
		}
	}

	applyFn := func(pX, pY []float64, coeff [][]float64) float64 {
		result := float64(0)
		for i := 0; i < N; i++ {
			r := LinearCombination(coeff[i], pY)
			result += r * pX[i]
		}
		return result
	}

	return func(x, y float64) (float64, error) {
		if x < 0 || x > 1 {
			return 0, errors.New("Out of range x 0 - 1.")
		}
		if y < 0 || y > 1 {
			return 0, errors.New("Out of range y 0 - 1.")
		}

		x2 := x * x
		x3 := x2 * x
		pX := []float64{1, x, x2, x3}

		y2 := y * y
		y3 := y2 * y
		pY := []float64{1, y, y2, y3}

		return applyFn(pX, pY, a), nil
	}
}

// Search a sorted set of values for c.
func searchIndex(c float64, val []float64) int {

  idx := sort.SearchFloat64s(val, c)

  // Too low or high. Return -1.
  if idx == 0 || idx >= len(val) {
    return -1
  }

  // The returned index is used as a rage. Ensure that idx+1 is in the array list.
  return idx - 1
}

func LinearCombination(a, b []float64) float64 {
	result := float64(0)
	for i := range a {
		result = result + a[i] * b[i]
	}
	return result
}
<commit_msg>bicubic: Adding some debugging.<commit_after>package math

import (
	"errors"
  "sort"
)

// A translation of Apache Commons Math.
// Returns an interpolation function, valid function, and an error if any.
func BicubicInterpolator(
	xval, yval []float64,
	fval [][]float64) (
	func(float64, float64) (float64, error),
	func(float64, float64) bool,
	error) {

	if len(xval) == 0 || len(yval) == 0 || len(fval) == 0 {
		return nil, nil, errors.New("An array is zero length")
	}

	// Check input order.
	for i := 1; i < len(xval); i++ {
		if xval[i] <= xval[i-1] {
			return nil, nil, errors.New("X values are not monotonically increasing.")
		}
	}

	for i := 1; i < len(yval); i++ {
		if yval[i] <= yval[i-1] {
			return nil, nil, errors.New("Y values are not monotonically increasing.")
		}
	}

	dFdX := make([][]float64, len(xval))
	dFdY := make([][]float64, len(xval))
	d2FdXdY := make([][]float64, len(xval))
	for i := 0; i < len(xval); i++ {
		dFdX[i] = make([]float64, len(yval))
		dFdY[i] = make([]float64, len(yval))
		d2FdXdY[i] = make([]float64, len(yval))
	}

	for i := 1; i < len(xval)-1; i++ {
		nI := i + 1
		pI := i - 1

		nX := xval[nI]
		pX := xval[pI]

		deltaX := nX - pX

		for j := 1; j < len(yval)-1; j++ {
			nJ := j + 1
			pJ := j - 1

			nY := yval[nJ]
			pY := yval[pJ]

			deltaY := nY - pY

			dFdX[i][j] = (fval[nI][j] - fval[pI][j]) / deltaX
			dFdY[i][j] = (fval[i][nJ] - fval[i][pJ]) / deltaY

			deltaXY := deltaX * deltaY

			d2FdXdY[j][j] = (fval[nI][nJ] - fval[nI][pJ] - fval[pI][nJ] + fval[pI][pJ]) / deltaXY
		}
	}

	isValidFn := func(x, y float64) bool {
		if x < xval[1] || x > xval[len(xval)-2] || y < yval[1] || y > yval[len(yval)-2] {
			// println("----------------")
			// println(x, xval[1], xval[len(xval)-2], y, yval[1], yval[len(yval)-2])
			// println("----------------")
			return false
		} else {
			return true
		}
	}

	interpolateFn, err := bicubicInterplationFunction(xval, yval, fval, dFdX, dFdY, d2FdXdY)
	if err != nil {
		return nil, nil, err
	}

	return interpolateFn, isValidFn, nil
}

func bicubicInterplationFunction(
	xval, yval []float64,
	f, dFdX, dFdY, d2FdXdY [][]float64) (func(x, y float64) (float64, error), error) {

	if len(xval) == 0 || len(yval) == 0 || len(f) == 0 || len(f[0]) == 0 {
		return nil, errors.New("Input length is zero.")
	}
	if len(xval) != len(f) {
		return nil, errors.New("X len does not equal f len.")
	}
	if len(xval) != len(dFdX) {
		return nil, errors.New("Dimensions don't match between x and dFDx.")
	}
	if len(xval) != len(dFdY) {
		return nil, errors.New("Dimensions don't match between x and dFDY.")
	}
	if len(xval) != len(d2FdXdY) {
		return nil, errors.New("Dimensions don't match between x and d2FdXdY.")
	}

	// We skip x and y order checks because it is done in the calling function.

	lastI := len(xval) - 1
	lastJ := len(yval) - 1
	splines := make([][]func(float64, float64) (float64, error), lastI)

	for i := 0; i < lastI; i++ {
		splines[i] = make([]func(float64, float64) (float64, error), lastJ)
		if len(f[i]) != len(yval) {
			return nil, errors.New("Dimension mismatch of f.")
		}
		if len(dFdX[i]) != len(yval) {
			return nil, errors.New("Dimension mismatch of dFdX.")
		}
		if len(dFdY[i]) != len(yval) {
			return nil, errors.New("Dimension mismatch of dFdY.")
		}
		if len(d2FdXdY[i]) != len(yval) {
			return nil, errors.New("Dimension mismatch of d2FdXdY[i].")
		}
		ip1 := i + 1
		xR := xval[ip1] - xval[i]
		for j := 0; j < lastJ; j++ {
			jp1 := j + 1
			yR := yval[jp1] - yval[j]
			xRyR := xR * yR
			beta := []float64{
				f[i][j], f[ip1][j], f[i][jp1], f[ip1][jp1],
				dFdX[i][j] * xR, dFdX[ip1][j] * xR, dFdX[i][jp1] * xR, dFdX[ip1][jp1] * xR,
				dFdY[i][j] * yR, dFdY[ip1][j] * yR, dFdY[i][jp1] * yR, dFdY[ip1][jp1] * yR,
				d2FdXdY[i][j] * xRyR, d2FdXdY[ip1][j] * xRyR, d2FdXdY[i][jp1] * xRyR, d2FdXdY[ip1][jp1] * xRyR}

			splines[i][j] = bicubicFunction(computeSplineCoefficients(beta))
		}
	}

  interpF := func(x, y float64)(float64, error) {
    i := searchIndex(x, xval)
    j := searchIndex(y, yval)

    xN := (x - xval[i]) / (xval[i+1] - xval[i])
    yN := (y - yval[j]) / (yval[j+1] - yval[j])

    return splines[i][j](xN, yN)
  }

	return interpF, nil
}

func computeSplineCoefficients(beta []float64) []float64 {
	NUM_COEFF := 16
	AINV := [][]float64{
		{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{-3, 3, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{2, -2, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, -2, -1, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 1, 1, 0, 0},
		{-3, 0, 3, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, -3, 0, 3, 0, 0, 0, 0, 0, -2, 0, -1, 0},
		{9, -9, -9, 9, 6, 3, -6, -3, 6, -6, 3, -3, 4, 2, 2, 1},
		{-6, 6, 6, -6, -3, -3, 3, 3, -4, 4, -2, 2, -2, -2, -1, -1},
		{2, 0, -2, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0, 0, 1, 0, 1, 0},
		{-6, 6, 6, -6, -4, -2, 4, 2, -3, 3, -3, 3, -2, -1, -2, -1},
		{4, -4, -4, 4, 2, 2, -2, -2, 2, -2, 2, -2, 1, 1, 1, 1}}

	a := make([]float64, NUM_COEFF)
	for i := 0; i < NUM_COEFF; i++ {
		result := float64(0)
		row := AINV[i]
		for j := 0; j < NUM_COEFF; j++ {
			result +=  (row[j] * beta[j])
		}
		a[i] = result
	}
	return a
}

func bicubicFunction(coeff []float64) func(float64, float64) (float64, error) {

	N := 4

	a := make([][]float64, N)
	for j := range a {
		a[j] = make([]float64, N)
		aJ := a[j]
		for i := range aJ {
			aJ[i] = coeff[i*N+j]
		}
	}

	applyFn := func(pX, pY []float64, coeff [][]float64) float64 {
		result := float64(0)
		for i := 0; i < N; i++ {
			r := LinearCombination(coeff[i], pY)
			result += r * pX[i]
		}
		return result
	}

	return func(x, y float64) (float64, error) {
		if x < 0 || x > 1 {
			return 0, errors.New("Out of range x 0 - 1.")
		}
		if y < 0 || y > 1 {
			return 0, errors.New("Out of range y 0 - 1.")
		}

		x2 := x * x
		x3 := x2 * x
		pX := []float64{1, x, x2, x3}

		y2 := y * y
		y3 := y2 * y
		pY := []float64{1, y, y2, y3}

		return applyFn(pX, pY, a), nil
	}
}

// Search a sorted set of values for c.
func searchIndex(c float64, val []float64) int {

  idx := sort.SearchFloat64s(val, c)

  // Too low or high. Return -1.
  if idx == 0 || idx >= len(val) {
    return -1
  }

  // The returned index is used as a rage. Ensure that idx+1 is in the array list.
  return idx - 1
}

func LinearCombination(a, b []float64) float64 {
	result := float64(0)
	for i := range a {
		result = result + a[i] * b[i]
	}
	return result
}
<|endoftext|>


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

package donna

import ()

var razoringMargin = [4]int{0, 512, 512+128, 512+256}
var futilityMargin = [4]int{0, 256, 512, 512+128}

// Search with zero window.
func (p *Position) searchWithZeroWindow(beta, depth int) int {
	p.game.nodes++
	if p.isRepetition() {
		return 0
	}

	ply := Ply()
	bestScore := ply - Checkmate
	if bestScore >= beta {
		return bestScore //beta
	}

	// Probe cache.
	cachedMove := Move(0)
	if cached := p.probeCache(); cached != nil {
		cachedMove = cached.move
		if cached.depth >= depth {
			score := cached.score
			if score > Checkmate - MaxPly && score <= Checkmate {
				score -= ply
			} else if score >= -Checkmate && score < -Checkmate + MaxPly {
				score += ply
			}

			if (cached.flags == cacheExact) ||
				(cached.flags == cacheBeta && score >= beta) ||
				(cached.flags == cacheAlpha && score <= beta) {
				return score
			}
		}
	}

	score := p.Evaluate()

	// Razoring and futility pruning. TODO: disable or tune-up in puzzle solving mode.
	if depth < len(razoringMargin) {
		if margin := beta - razoringMargin[depth]; score < margin && beta < Checkmate - MaxPly && cachedMove == Move(0) {
			if p.outposts[pawn(p.color)]&mask7th[p.color] == 0 { // No pawns on 7th.
				razorScore := p.searchQuiescence(margin-1, margin)
				if razorScore < margin {
					return razorScore
				}
			}
		}

		if margin := score - futilityMargin[depth]; margin >= beta && beta > -Checkmate + MaxPly {
			if p.outposts[p.color] & ^p.outposts[king(p.color)] & ^p.outposts[pawn(p.color)] != 0 {
				return margin
			}
		}
	}

	// Null move pruning.
	if depth > 1 && score >= beta && p.outposts[p.color].count() > 5 && !p.isNull() {
		reduction := 3 + depth/4
		if score-100 > beta {
			reduction++
		}

		position := p.MakeNullMove()
		if depth <= reduction {
			score = -position.searchQuiescence(-beta, 1-beta)
		} else {
			score = -position.searchWithZeroWindow(1-beta, depth-reduction)
		}
		position.TakeBackNullMove()

		if score >= beta {
			return score
		}
	}

	moveCount := 0
	gen := NewGen(p, ply).generateMoves().rank(cachedMove)
	for move := gen.NextMove(); move != 0; move = gen.NextMove() {
		if position := p.MakeMove(move); position != nil {
			//Log("%*szero/%s> depth: %d, ply: %d, move: %s\n", ply*2, ` `, C(p.color), depth, ply, move)
			inCheck, giveCheck := position.isInCheck(position.color), position.isInCheck(position.color^1)

			reducedDepth := depth
			if !inCheck && !giveCheck && move&(isCapture|isPromo) == 0 && depth >= 3 && moveCount >= 8 {
				reducedDepth = depth - 2 // Late move reduction. TODO: disable or tune-up in puzzle solving mode.
				if reducedDepth > 0 && moveCount >= 16 {
					reducedDepth--
					if reducedDepth > 0 && moveCount >= 32 {
						reducedDepth--
					}
				}
			} else if !inCheck {
				reducedDepth = depth - 1
			}

			moveScore := 0
			if reducedDepth == 0 {
				moveScore = -position.searchQuiescence(-beta, 1-beta)
			} else if inCheck {
				moveScore = -position.searchInCheck(1-beta, reducedDepth)
			} else {
				moveScore = -position.searchWithZeroWindow(1-beta, reducedDepth)

				// Verify late move reduction.
				if reducedDepth < depth-1 && moveScore >= beta {
					moveScore = -position.searchWithZeroWindow(1-beta, depth-1)
				}
			}

			position.TakeBack(move)
			moveCount++

			if moveScore > bestScore {
				if moveScore >= beta {
					p.cache(move, moveScore, depth, cacheBeta)
					p.game.saveGood(depth, move)
					return moveScore
				}
				bestScore = moveScore
			}
		}
	} // next move.

	if moveCount == 0 {
		return 0
	}

	return bestScore
}
<commit_msg>IID and better LMR<commit_after>// Copyright (c) 2013-2014 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

import ()

var razoringMargin = [4]int{0, 512, 512+128, 512+256}
var futilityMargin = [4]int{0, 256, 512, 512+128}

// Search with zero window.
func (p *Position) searchWithZeroWindow(beta, depth int) int {
	p.game.nodes++
	if p.isRepetition() {
		return 0
	}

	ply := Ply()
	p.game.pvsize[ply] = ply

	bestScore := ply - Checkmate
	if bestScore >= beta {
		return bestScore //beta
	}

	// Probe cache.
	cachedMove := Move(0)
	if cached := p.probeCache(); cached != nil {
		cachedMove = cached.move
		if cached.depth >= depth {
			score := cached.score
			if score > Checkmate - MaxPly && score <= Checkmate {
				score -= ply
			} else if score >= -Checkmate && score < -Checkmate + MaxPly {
				score += ply
			}

			if (cached.flags == cacheExact) ||
				(cached.flags == cacheBeta && score >= beta) ||
				(cached.flags == cacheAlpha && score <= beta) {
				return score
			}
		}
	}

	score := p.Evaluate()

	// Razoring and futility pruning. TODO: disable or tune-up in puzzle solving mode.
	if depth < len(razoringMargin) {
		if margin := beta - razoringMargin[depth]; score < margin && beta < Checkmate - MaxPly && cachedMove == Move(0) {
			if p.outposts[pawn(p.color)]&mask7th[p.color] == 0 { // No pawns on 7th.
				razorScore := p.searchQuiescence(margin-1, margin)
				if razorScore < margin {
					return razorScore
				}
			}
		}

		if margin := score - futilityMargin[depth]; margin >= beta && beta > -Checkmate + MaxPly {
			if p.outposts[p.color] & ^p.outposts[king(p.color)] & ^p.outposts[pawn(p.color)] != 0 {
				return margin
			}
		}
	}

	// Null move pruning.
	if depth > 1 && score >= beta && p.outposts[p.color].count() > 5 && !p.isNull() {
		reduction := 3 + depth/4
		if score-100 > beta {
			reduction++
		}

		position := p.MakeNullMove()
		if depth <= reduction {
			score = -position.searchQuiescence(-beta, 1-beta)
		} else {
			score = -position.searchWithZeroWindow(1-beta, depth-reduction)
		}
		position.TakeBackNullMove()

		if score >= beta {
			return score
		}
	}

	// Internal iterative deepening.
	if cachedMove == 0 && depth > 4 {
		p.searchWithZeroWindow(beta, depth - 4)
		if p.game.pvsize[ply] > 0 {
			cachedMove = p.game.pv[ply][0]
		}
	}

	moveCount := 0
	gen := NewGen(p, ply).generateMoves().rank(cachedMove)
	for move := gen.NextMove(); move != 0; move = gen.NextMove() {
		if position := p.MakeMove(move); position != nil {
			//Log("%*szero/%s> depth: %d, ply: %d, move: %s\n", ply*2, ` `, C(p.color), depth, ply, move)
			inCheck := position.isInCheck(position.color)

			// Late move reduction.
			reducedDepth := depth
			if !inCheck {
				reducedDepth--
				giveCheck := position.isInCheck(position.color^1)
				if !giveCheck && depth >= 3 && moveCount >= 12 && move & (isCapture | isPromo) == 0 {
					reducedDepth--
					if reducedDepth > 0 && moveCount >= 18 {
						reducedDepth--
						if reducedDepth > 0 && moveCount >= 24 {
							reducedDepth--
						}
					}
				}
			}

			moveScore := 0
			if reducedDepth == 0 {
				moveScore = -position.searchQuiescence(-beta, 1-beta)
			} else if inCheck {
				moveScore = -position.searchInCheck(1-beta, reducedDepth)
			} else {
				moveScore = -position.searchWithZeroWindow(1-beta, reducedDepth)

				// Verify late move reduction.
				if reducedDepth < depth-1 && moveScore >= beta {
					moveScore = -position.searchWithZeroWindow(1-beta, depth-1)
				}
			}

			position.TakeBack(move)
			moveCount++

			if moveScore > bestScore {
				p.game.saveBest(ply, move)
				if moveScore >= beta {
					p.cache(move, moveScore, depth, cacheBeta)
					p.game.saveGood(depth, move)
					return moveScore
				}
				bestScore = moveScore
			}
		}
	} // next move.

	if moveCount == 0 {
		return 0
	}

	return bestScore
}
<|endoftext|>


<commit_before>// Copyright 2015 Martin Hebnes Pedersen (LA5NTA). All rights reserved.
// Use of this source code is governed by the MIT-license that can be
// found in the LICENSE file.

package ardop

import (
	"fmt"
	"io"
	"net"
)

type listener struct {
	incoming <-chan net.Conn
	quit     chan struct{}
	errors   <-chan error
	addr     Addr
}

func (l listener) Accept() (c net.Conn, err error) {
	select {
	case c, ok := <-l.incoming:
		if !ok {
			return nil, io.EOF
		}
		return c, nil
	case err = <-l.errors:
		return nil, err
	}
}

func (l listener) Addr() net.Addr {
	return l.addr
}

func (l listener) Close() error {
	close(l.quit)
	return nil
}

func (tnc *TNC) Listen() (ln net.Listener, err error) {
	if tnc.closed {
		return nil, ErrTNCClosed
	}

	if tnc.listenerActive {
		return nil, ErrActiveListenerExists
	}
	tnc.listenerActive = true

	incoming := make(chan net.Conn)
	quit := make(chan struct{})
	errors := make(chan error)

	mycall, err := tnc.MyCall()
	if err != nil {
		return nil, fmt.Errorf("Unable to get mycall: %s", err)
	}

	if err := tnc.SetListenEnabled(true); err != nil {
		return nil, fmt.Errorf("TNC failed to enable listening: %s", err)
	}

	go func() {
		defer func() {
			close(incoming) // Important to close this first!
			close(errors)
			tnc.listenerActive = false
		}()

		msgListener := tnc.in.Listen()
		msgs := msgListener.Msgs()

		var remotecall, targetcall string
		for {
			select {
			case <-quit:
				tnc.SetListenEnabled(false) // Should return this in listener.Close()
				errors <- fmt.Errorf("Closed")
				return
			case msg, ok := <-msgs:
				switch {
				case !ok:
					errors <- ErrTNCClosed
					return
				case msg.cmd == cmdCancelPending:
					remotecall, targetcall = "", ""
				case msg.cmd == cmdConnected:
					remotecall = msg.value.([]string)[0]
				case msg.cmd == cmdTarget:
					targetcall = msg.String()
				}

				if len(remotecall) > 0 && len(targetcall) > 0 {
					tnc.data = &tncConn{
						remoteAddr: Addr{remotecall},
						localAddr:  Addr{targetcall},
						ctrlOut:    tnc.out,
						dataOut:    tnc.dataOut,
						ctrlIn:     tnc.in,
						dataIn:     tnc.dataIn,
						eofChan:    make(chan struct{}),
						doCRC:      tnc.doCRC,
					}

					tnc.connected = true
					incoming <- tnc.data

					remotecall, targetcall = "", ""
				}
			}
		}
	}()

	return listener{incoming, quit, errors, Addr{mycall}}, nil
}
<commit_msg>ardop: Close broadcast receiver on listener close<commit_after>// Copyright 2015 Martin Hebnes Pedersen (LA5NTA). All rights reserved.
// Use of this source code is governed by the MIT-license that can be
// found in the LICENSE file.

package ardop

import (
	"fmt"
	"io"
	"net"
)

type listener struct {
	incoming <-chan net.Conn
	quit     chan struct{}
	errors   <-chan error
	addr     Addr
}

func (l listener) Accept() (c net.Conn, err error) {
	select {
	case c, ok := <-l.incoming:
		if !ok {
			return nil, io.EOF
		}
		return c, nil
	case err = <-l.errors:
		return nil, err
	}
}

func (l listener) Addr() net.Addr {
	return l.addr
}

func (l listener) Close() error {
	close(l.quit)
	return nil
}

func (tnc *TNC) Listen() (ln net.Listener, err error) {
	if tnc.closed {
		return nil, ErrTNCClosed
	}

	if tnc.listenerActive {
		return nil, ErrActiveListenerExists
	}
	tnc.listenerActive = true

	incoming := make(chan net.Conn)
	quit := make(chan struct{})
	errors := make(chan error)

	mycall, err := tnc.MyCall()
	if err != nil {
		return nil, fmt.Errorf("Unable to get mycall: %s", err)
	}

	if err := tnc.SetListenEnabled(true); err != nil {
		return nil, fmt.Errorf("TNC failed to enable listening: %s", err)
	}

	go func() {
		defer func() {
			close(incoming) // Important to close this first!
			close(errors)
			tnc.listenerActive = false
		}()

		msgListener := tnc.in.Listen()
		defer msgListener.Close()
		msgs := msgListener.Msgs()

		var remotecall, targetcall string
		for {
			select {
			case <-quit:
				tnc.SetListenEnabled(false) // Should return this in listener.Close()
				errors <- fmt.Errorf("Closed")
				return
			case msg, ok := <-msgs:
				switch {
				case !ok:
					errors <- ErrTNCClosed
					return
				case msg.cmd == cmdCancelPending:
					remotecall, targetcall = "", ""
				case msg.cmd == cmdConnected:
					remotecall = msg.value.([]string)[0]
				case msg.cmd == cmdTarget:
					targetcall = msg.String()
				}

				if len(remotecall) > 0 && len(targetcall) > 0 {
					tnc.data = &tncConn{
						remoteAddr: Addr{remotecall},
						localAddr:  Addr{targetcall},
						ctrlOut:    tnc.out,
						dataOut:    tnc.dataOut,
						ctrlIn:     tnc.in,
						dataIn:     tnc.dataIn,
						eofChan:    make(chan struct{}),
						doCRC:      tnc.doCRC,
					}

					tnc.connected = true
					incoming <- tnc.data

					remotecall, targetcall = "", ""
				}
			}
		}
	}()

	return listener{incoming, quit, errors, Addr{mycall}}, nil
}
<|endoftext|>