<commit_before>package gcloud

import (
	"context"
	"errors"
	"fmt"
	"log"
	"net"
	"regexp"
	"time"

	"golang.org/x/oauth2/google"

	"github.com/czerwonk/dns-drain/changelog"

	dns "google.golang.org/api/dns/v1"
)

type GoogleDnsDrainer struct {
	project    string
	dryRun     bool
	zoneFilter *regexp.Regexp
	skipFilter *regexp.Regexp
	service    *dns.Service
	logger     changelog.ChangeLogger
	updater    *recordUpdater
}

func NewDrainer(project string, dryRun bool, zoneFilter *regexp.Regexp, skipFilter *regexp.Regexp, changelogger changelog.ChangeLogger) *GoogleDnsDrainer {
	return &GoogleDnsDrainer{project: project, dryRun: dryRun, zoneFilter: zoneFilter, skipFilter: skipFilter, logger: changelogger}
}

func (client *GoogleDnsDrainer) Drain(ipNet *net.IPNet, newIp net.IP) error {
	ctx := context.Background()
	c, err := google.DefaultClient(ctx, dns.CloudPlatformScope)
	if err != nil {
		return err
	}

	client.service, err = dns.New(c)
	if err != nil {
		return err
	}

	client.updater = &recordUpdater{service: client.service, project: client.project, dryRun: client.dryRun}

	zones, err := client.getZones()
	if err != nil {
		return err
	}

	done := make(chan bool)
	for _, z := range zones {
		go client.drainWithIpNet(z.Name, ipNet, newIp, done)
	}

	for i := 0; i < len(zones); i++ {
		select {
		case <-done:
		case <-time.After(60 * time.Second):
			return errors.New(fmt.Sprintln("Timeout exceeded!"))
		}
	}

	return nil
}

func (client *GoogleDnsDrainer) getZones() ([]*dns.ManagedZone, error) {
	r, err := client.service.ManagedZones.List(client.project).Do()
	if err != nil {
		return nil, err
	}

	zones := make([]*dns.ManagedZone, 0)
	for _, z := range r.ManagedZones {
		if !client.matchesSkipFilter(z.Name) && client.matchesZoneFilter(z.Name) {
			zones = append(zones, z)
		}
	}

	return zones, nil
}

func (client *GoogleDnsDrainer) matchesSkipFilter(zone string) bool {
	return client.skipFilter != nil && client.skipFilter.MatchString(zone)
}

func (client *GoogleDnsDrainer) matchesZoneFilter(zone string) bool {
	return client.zoneFilter == nil || client.zoneFilter.MatchString(zone)
}

func (client *GoogleDnsDrainer) drainWithIpNet(zone string, ipNet *net.IPNet, newIp net.IP, done chan bool) {
	defer func() { done <- true }()

	r, err := client.service.ResourceRecordSets.List(client.project, zone).Do()
	if err != nil {
		log.Printf("ERROR - %s: %s\n", zone, err)
		return
	}

	for _, rec := range r.Rrsets {
		client.handleRecordSet(zone, rec, ipNet, newIp)
	}
}

func (client *GoogleDnsDrainer) handleRecordSet(zone string, rec *dns.ResourceRecordSet, ipNet *net.IPNet, newIp net.IP) {
	d := filterWithIpNet(rec, ipNet)

	if len(d) == 0 && newIp == nil {
		log.Printf("WARN - %s %s: Only one IP assigned to record. Can not drain!\n", rec.Type, rec.Name)
		return
	}

	if len(d) == len(rec.Rrdatas) {
		return
	}

	if newIp != nil && !isInDatas(newIp, d) {
		d = append(d, newIp.String())
	}

	err := client.updateRecordSet(rec, zone, d)
	if err != nil {
		log.Printf("ERROR - %s: %s", rec.Name, err)
	}
}

func filterWithIpNet(rec *dns.ResourceRecordSet, ipNet *net.IPNet) []string {
	res := make([]string, 0)

	for _, x := range rec.Rrdatas {
		ip := net.ParseIP(x)
		if ip == nil || !ipNet.Contains(ip) {
			res = append(res, x)
		}
	}

	return res
}

func isInDatas(ip net.IP, datas []string) bool {
	for _, x := range datas {
		if x == ip.String() {
			return true
		}
	}

	return false
}

func (client *GoogleDnsDrainer) updateRecordSet(rec *dns.ResourceRecordSet, zone string, datas []string) error {
	err := client.updater.updateRecordSet(zone, rec, datas)
	if err != nil {
		return err
	}

	return client.logChanges(rec, zone, datas)
}

func (client *GoogleDnsDrainer) logChanges(rec *dns.ResourceRecordSet, zone string, datas []string) error {
	before := rec.Rrdatas
	after := datas

	m := make(map[string]int)
	for _, x := range before {
		m[x] = -1
	}
	for _, y := range after {
		m[y] += 1
	}

	for k, v := range m {
		if v != 0 {
			err := client.logChange(rec, zone, k, v)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func (client *GoogleDnsDrainer) logChange(rec *dns.ResourceRecordSet, zone string, value string, changeValue int) error {
	var action string
	if changeValue == 1 {
		action = changelog.Add
	} else {
		action = changelog.Remove
	}

	c := changelog.DnsChange{Provider: "gcloud", Zone: zone, Record: rec.Name, RecordType: rec.Type, Value: value, Action: action}
	return client.logger.LogChange(c)
}
<commit_msg>prepared second operation mode<commit_after>package gcloud

import (
	"context"
	"errors"
	"fmt"
	"log"
	"net"
	"regexp"
	"time"

	"golang.org/x/oauth2/google"

	"github.com/czerwonk/dns-drain/changelog"

	dns "google.golang.org/api/dns/v1"
)

type GoogleDnsDrainer struct {
	project    string
	dryRun     bool
	zoneFilter *regexp.Regexp
	skipFilter *regexp.Regexp
	service    *dns.Service
	logger     changelog.ChangeLogger
	updater    *recordUpdater
}

type ZoneHandlerFunc func(*dns.ManagedZone, chan bool)

func NewDrainer(project string, dryRun bool, zoneFilter *regexp.Regexp, skipFilter *regexp.Regexp, changelogger changelog.ChangeLogger) *GoogleDnsDrainer {
	return &GoogleDnsDrainer{project: project, dryRun: dryRun, zoneFilter: zoneFilter, skipFilter: skipFilter, logger: changelogger}
}

func (client *GoogleDnsDrainer) Drain(ipNet *net.IPNet, newIp net.IP) error {
	handlerFunc := func(z *dns.ManagedZone, done chan bool) {
		client.drainWithIpNet(z.Name, ipNet, newIp, done)
	}

	return client.performForZones(handlerFunc)
}

func (client *GoogleDnsDrainer) performForZones(handlerFunc ZoneHandlerFunc) error {
	ctx := context.Background()
	c, err := google.DefaultClient(ctx, dns.CloudPlatformScope)
	if err != nil {
		return err
	}

	client.service, err = dns.New(c)
	if err != nil {
		return err
	}

	client.updater = &recordUpdater{service: client.service, project: client.project, dryRun: client.dryRun}

	zones, err := client.getZones()
	if err != nil {
		return err
	}

	done := make(chan bool)
	for _, z := range zones {
		go handlerFunc(z, done)
	}

	for i := 0; i < len(zones); i++ {
		select {
		case <-done:
		case <-time.After(60 * time.Second):
			return errors.New(fmt.Sprintln("Timeout exceeded!"))
		}
	}

	return nil
}

func (client *GoogleDnsDrainer) getZones() ([]*dns.ManagedZone, error) {
	r, err := client.service.ManagedZones.List(client.project).Do()
	if err != nil {
		return nil, err
	}

	zones := make([]*dns.ManagedZone, 0)
	for _, z := range r.ManagedZones {
		if !client.matchesSkipFilter(z.Name) && client.matchesZoneFilter(z.Name) {
			zones = append(zones, z)
		}
	}

	return zones, nil
}

func (client *GoogleDnsDrainer) matchesSkipFilter(zone string) bool {
	return client.skipFilter != nil && client.skipFilter.MatchString(zone)
}

func (client *GoogleDnsDrainer) matchesZoneFilter(zone string) bool {
	return client.zoneFilter == nil || client.zoneFilter.MatchString(zone)
}

func (client *GoogleDnsDrainer) drainWithIpNet(zone string, ipNet *net.IPNet, newIp net.IP, done chan bool) {
	defer func() { done <- true }()

	r, err := client.service.ResourceRecordSets.List(client.project, zone).Do()
	if err != nil {
		log.Printf("ERROR - %s: %s\n", zone, err)
		return
	}

	for _, rec := range r.Rrsets {
		client.handleRecordSet(zone, rec, ipNet, newIp)
	}
}

func (client *GoogleDnsDrainer) handleRecordSet(zone string, rec *dns.ResourceRecordSet, ipNet *net.IPNet, newIp net.IP) {
	d := filterWithIpNet(rec, ipNet)

	if len(d) == 0 && newIp == nil {
		log.Printf("WARN - %s %s: Only one IP assigned to record. Can not drain!\n", rec.Type, rec.Name)
		return
	}

	if len(d) == len(rec.Rrdatas) {
		return
	}

	if newIp != nil && !isInDatas(newIp, d) {
		d = append(d, newIp.String())
	}

	err := client.updateRecordSet(rec, zone, d)
	if err != nil {
		log.Printf("ERROR - %s: %s", rec.Name, err)
	}
}

func filterWithIpNet(rec *dns.ResourceRecordSet, ipNet *net.IPNet) []string {
	res := make([]string, 0)

	for _, x := range rec.Rrdatas {
		ip := net.ParseIP(x)
		if ip == nil || !ipNet.Contains(ip) {
			res = append(res, x)
		}
	}

	return res
}

func isInDatas(ip net.IP, datas []string) bool {
	for _, x := range datas {
		if x == ip.String() {
			return true
		}
	}

	return false
}

func (client *GoogleDnsDrainer) updateRecordSet(rec *dns.ResourceRecordSet, zone string, datas []string) error {
	err := client.updater.updateRecordSet(zone, rec, datas)
	if err != nil {
		return err
	}

	return client.logChanges(rec, zone, datas)
}

func (client *GoogleDnsDrainer) logChanges(rec *dns.ResourceRecordSet, zone string, datas []string) error {
	before := rec.Rrdatas
	after := datas

	m := make(map[string]int)
	for _, x := range before {
		m[x] = -1
	}
	for _, y := range after {
		m[y] += 1
	}

	for k, v := range m {
		if v != 0 {
			err := client.logChange(rec, zone, k, v)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func (client *GoogleDnsDrainer) logChange(rec *dns.ResourceRecordSet, zone string, value string, changeValue int) error {
	var action string
	if changeValue == 1 {
		action = changelog.Add
	} else {
		action = changelog.Remove
	}

	c := changelog.DnsChange{Provider: "gcloud", Zone: zone, Record: rec.Name, RecordType: rec.Type, Value: value, Action: action}
	return client.logger.LogChange(c)
}
<|endoftext|>


<commit_before>package gen

import (
	"reflect"

	"github.com/leanovate/gopter"
)

type nilShrink struct {
	done bool
}

func (s *nilShrink) Next() (interface{}, bool) {
	if !s.done {
		s.done = true
		return nil, true
	}
	return nil, false
}

// PtrShrinker convert a value shrinker to a pointer to value shrinker
func PtrShrinker(elementShrinker gopter.Shrinker) gopter.Shrinker {
	return func(v interface{}) gopter.Shrink {
		if v == nil {
			return gopter.NoShrink
		}
		rt := reflect.TypeOf(v)
		elementShink := elementShrinker(reflect.ValueOf(v).Elem().Interface())

		nilShrink := &nilShrink{}
		return gopter.ConcatShrinks(
			nilShrink.Next,
			elementShink.Map(func(elem interface{}) interface{} {
				slice := reflect.MakeSlice(reflect.SliceOf(rt.Elem()), 0, 1)
				slice = reflect.Append(slice, reflect.ValueOf(elem))

				return slice.Index(0).Addr().Interface()
			}),
		)
	}
}
<commit_msg>Addresses #40<commit_after>package gen

import (
	"reflect"

	"github.com/leanovate/gopter"
)

type nilShrink struct {
	done bool
}

func (s *nilShrink) Next() (interface{}, bool) {
	if !s.done {
		s.done = true
		return nil, true
	}
	return nil, false
}

// PtrShrinker convert a value shrinker to a pointer to value shrinker
func PtrShrinker(elementShrinker gopter.Shrinker) gopter.Shrinker {
	return func(v interface{}) gopter.Shrink {
		if v == nil {
			return gopter.NoShrink
		}
		elem := reflect.ValueOf(v).Elem()
		if !elem.IsValid() || !elem.CanInterface() {
			return gopter.NoShrink
		}
		rt := reflect.TypeOf(v)
		elementShink := elementShrinker(reflect.ValueOf(v).Elem().Interface())

		nilShrink := &nilShrink{}
		return gopter.ConcatShrinks(
			nilShrink.Next,
			elementShink.Map(func(elem interface{}) interface{} {
				slice := reflect.MakeSlice(reflect.SliceOf(rt.Elem()), 0, 1)
				slice = reflect.Append(slice, reflect.ValueOf(elem))

				return slice.Index(0).Addr().Interface()
			}),
		)
	}
}
<|endoftext|>


<commit_before>package generator

import (
	"fmt"
	"gopkg.in/yaml.v2"
	"log"
	"os"
	"reflect"
	"strconv"
	"regexp"
)

type Chart struct {
	ApiVersion  string `yaml:"apiVersion"`
	Name        string `yaml:"name"`
	Version     string `yaml:"version"`
	KubeVersion string `yaml:"kubeVersion"`
	Description string `yaml:"description"`
	Type        string `yaml:"type"`
	Icon        string `yaml:"icon"`
}

type kube struct {
	Parameters []struct {
		Name        string `yaml:"name"`
		Description string `yaml:"description"`
		Required    bool   `yaml:"required"`
		Value       string `yaml:"value"`
	}
	Objects []object
}

type object struct {
	ApiVersion string                 `yaml:"apiVersion"`
	Kind       string                 `yaml:"kind"`
	Metadata   map[string]interface{} //metadata has a unkown structure so we use a generic interface
	Spec       map[string]interface{} `yaml:"spec,omitempty"` //specs are dependent on the kind so we use a generic interface
	Data       map[string]interface{} `yaml:"data,omitempty"` //specs are dependent on the kind so we use a generic interface
}

func createFolderStruct(path string) {
	var chartsFldr string
	var templatesFldr string

	chartsFldr = path + "/charts"
	templatesFldr = path + "/templates"

	if _, err := os.Stat(path); os.IsNotExist(err) {
		os.Mkdir(path, 0700)
	}
	if _, err := os.Stat(chartsFldr); os.IsNotExist(err) {
		os.Mkdir(chartsFldr, 0700)
	}
	if _, err := os.Stat(templatesFldr); os.IsNotExist(err) {
		os.Mkdir(templatesFldr, 0700)
	}
}

func walk(input interface{}) {

	va := reflect.ValueOf(&input).Elem()
	val := va.Elem()

	switch val.Kind() {
	case reflect.Map:
		fmt.Println("***** MAP *******")
		fmt.Println(val)

		for i, k := range val.MapKeys() {
			v := val.MapIndex(k)
			fmt.Println(i, k, v)
			fmt.Println("!!!!!!!! CAN SET: ", v.CanSet())
			walk(v.Interface())

		}
		fmt.Println("CAN SET: ", val.CanSet())
	case reflect.String:
		fmt.Println("***** STRING *******")
		fmt.Println(val)
		fmt.Println("CAN SET: ", val.CanSet())
	default:
		fmt.Println("**** OTHER: ", val.Kind())
		fmt.Println(val)
		fmt.Println("CAN SET: ", val.CanSet())

	}

	/*

		val := reflect.ValueOf(input)

		if val.Kind() == reflect.Map {
			//fmt.Println("MAP!!!!")
			for i, k := range val.MapKeys() {
				v := val.MapIndex(k)
				fmt.Println(i, k, v)
				fmt.Println(val.CanSet())
				walk(v.Interface())

			}
		} else {

			switch val.Kind() {

			case reflect.String:
				fmt.Println("*** FOUND STRING ***")
				fmt.Println(val)
				fmt.Println(val.CanSet())
				fmt.Println("********************")

			default:
				fmt.Println(val.Kind())
			}

		}
	*/
}

func mod(o []byte) []byte  {
	str1 := string(o)

	var re = regexp.MustCompile(`(?m)\${([^}]*)}`)

    var substitution = "{{.Values.$1}}"
    str1 = re.ReplaceAllString(str1, substitution)
	//str1 = strings.Replace(str1, "${", "{{.Values.", -1)
	//str1 = strings.Replace(str1, "}", "}}", -1)
	return []byte(str1)
}

func genTemplate(objects kube, path string) {
	for x, y := range objects.Objects {

		//get the contents from the object
		content, err := yaml.Marshal(y)
		if err != nil {
			log.Fatal(err)
		}

		//convert the iteration into a string to be used in the filename
		no := strconv.Itoa(x)
		file, err := os.Create(path + "/templates/" + no + "-" + y.Kind + ".yaml")
		if err != nil {
			log.Fatal(err)
		}
		if _, err := file.Write(mod(content)); err != nil {
			log.Fatal(err)
		}
		if err := file.Close(); err != nil {
			log.Fatal(err)
		}
	}
}

func genChart(path string) {
	var c Chart
	c.ApiVersion = "v2"
	c.Name = "test"
	c.Version = "1.0.0"

	cg, err := yaml.Marshal(&c)
	if err != nil {
		log.Fatal(err)
	}

	chartFile, err := os.Create(path + "/Chart.yaml")
	if err != nil {
		log.Fatal(err)
	}

	if _, err := chartFile.Write(cg); err != nil {
		log.Fatal(err)
	}
	if err := chartFile.Close(); err != nil {
		log.Fatal(err)
	}
}

func genValues(parameters kube, path string) {
	m := make(map[interface{}]interface{})

	for _, y := range parameters.Parameters {
		m[y.Name] = y.Value
	}

	content, err := yaml.Marshal(m)
	if err != nil {
		log.Fatal(err)
	}

	file, err := os.Create(path + "/values.yaml")
	if err != nil {
		log.Fatal(err)
	}

	if _, err := file.Write(content); err != nil {
		log.Fatal(err)
	}
}

func Generate(path string, input []byte) {
	createFolderStruct(path)

	var data kube
	err := yaml.Unmarshal(input, &data)
	if err != nil {
		fmt.Println(err)
	}

	genTemplate(data, path)
	genValues(data, path)
	genChart(path)
}
<commit_msg>Format<commit_after>package generator

import (
	"fmt"
	"gopkg.in/yaml.v2"
	"log"
	"os"
	"reflect"
	"regexp"
	"strconv"
)

type Chart struct {
	ApiVersion  string `yaml:"apiVersion"`
	Name        string `yaml:"name"`
	Version     string `yaml:"version"`
	KubeVersion string `yaml:"kubeVersion"`
	Description string `yaml:"description"`
	Type        string `yaml:"type"`
	Icon        string `yaml:"icon"`
}

type kube struct {
	Parameters []struct {
		Name        string `yaml:"name"`
		Description string `yaml:"description"`
		Required    bool   `yaml:"required"`
		Value       string `yaml:"value"`
	}
	Objects []object
}

type object struct {
	ApiVersion string                 `yaml:"apiVersion"`
	Kind       string                 `yaml:"kind"`
	Metadata   map[string]interface{} //metadata has a unkown structure so we use a generic interface
	Spec       map[string]interface{} `yaml:"spec,omitempty"` //specs are dependent on the kind so we use a generic interface
	Data       map[string]interface{} `yaml:"data,omitempty"` //specs are dependent on the kind so we use a generic interface
}

func createFolderStruct(path string) {
	var chartsFldr string
	var templatesFldr string

	chartsFldr = path + "/charts"
	templatesFldr = path + "/templates"

	if _, err := os.Stat(path); os.IsNotExist(err) {
		os.Mkdir(path, 0700)
	}
	if _, err := os.Stat(chartsFldr); os.IsNotExist(err) {
		os.Mkdir(chartsFldr, 0700)
	}
	if _, err := os.Stat(templatesFldr); os.IsNotExist(err) {
		os.Mkdir(templatesFldr, 0700)
	}
}

func walk(input interface{}) {

	va := reflect.ValueOf(&input).Elem()
	val := va.Elem()

	switch val.Kind() {
	case reflect.Map:
		fmt.Println("***** MAP *******")
		fmt.Println(val)

		for i, k := range val.MapKeys() {
			v := val.MapIndex(k)
			fmt.Println(i, k, v)
			fmt.Println("!!!!!!!! CAN SET: ", v.CanSet())
			walk(v.Interface())

		}
		fmt.Println("CAN SET: ", val.CanSet())
	case reflect.String:
		fmt.Println("***** STRING *******")
		fmt.Println(val)
		fmt.Println("CAN SET: ", val.CanSet())
	default:
		fmt.Println("**** OTHER: ", val.Kind())
		fmt.Println(val)
		fmt.Println("CAN SET: ", val.CanSet())

	}

	/*

		val := reflect.ValueOf(input)

		if val.Kind() == reflect.Map {
			//fmt.Println("MAP!!!!")
			for i, k := range val.MapKeys() {
				v := val.MapIndex(k)
				fmt.Println(i, k, v)
				fmt.Println(val.CanSet())
				walk(v.Interface())

			}
		} else {

			switch val.Kind() {

			case reflect.String:
				fmt.Println("*** FOUND STRING ***")
				fmt.Println(val)
				fmt.Println(val.CanSet())
				fmt.Println("********************")

			default:
				fmt.Println(val.Kind())
			}

		}
	*/
}

func mod(o []byte) []byte {
	str1 := string(o)

	var re = regexp.MustCompile(`(?m)\${([^}]*)}`)

	var substitution = "{{.Values.$1}}"
	str1 = re.ReplaceAllString(str1, substitution)
	//str1 = strings.Replace(str1, "${", "{{.Values.", -1)
	//str1 = strings.Replace(str1, "}", "}}", -1)
	return []byte(str1)
}

func genTemplate(objects kube, path string) {
	for x, y := range objects.Objects {

		//get the contents from the object
		content, err := yaml.Marshal(y)
		if err != nil {
			log.Fatal(err)
		}

		//convert the iteration into a string to be used in the filename
		no := strconv.Itoa(x)
		file, err := os.Create(path + "/templates/" + no + "-" + y.Kind + ".yaml")
		if err != nil {
			log.Fatal(err)
		}
		if _, err := file.Write(mod(content)); err != nil {
			log.Fatal(err)
		}
		if err := file.Close(); err != nil {
			log.Fatal(err)
		}
	}
}

func genChart(path string) {
	var c Chart
	c.ApiVersion = "v2"
	c.Name = "test"
	c.Version = "1.0.0"

	cg, err := yaml.Marshal(&c)
	if err != nil {
		log.Fatal(err)
	}

	chartFile, err := os.Create(path + "/Chart.yaml")
	if err != nil {
		log.Fatal(err)
	}

	if _, err := chartFile.Write(cg); err != nil {
		log.Fatal(err)
	}
	if err := chartFile.Close(); err != nil {
		log.Fatal(err)
	}
}

func genValues(parameters kube, path string) {
	m := make(map[interface{}]interface{})

	for _, y := range parameters.Parameters {
		m[y.Name] = y.Value
	}

	content, err := yaml.Marshal(m)
	if err != nil {
		log.Fatal(err)
	}

	file, err := os.Create(path + "/values.yaml")
	if err != nil {
		log.Fatal(err)
	}

	if _, err := file.Write(content); err != nil {
		log.Fatal(err)
	}
}

func Generate(path string, input []byte) {
	createFolderStruct(path)

	var data kube
	err := yaml.Unmarshal(input, &data)
	if err != nil {
		fmt.Println(err)
	}

	genTemplate(data, path)
	genValues(data, path)
	genChart(path)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math/rand"
	"time"

	"github.com/gonum/matrix/mat64"
	"github.com/gonum/stat/distuv"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

func printMatrix(m mat64.Matrix, cols, decs int) {
	formatString := fmt.Sprintf("%%%d.%df", cols, decs)
	r, c := m.Dims()
	for i := 0; i < r; i++ {
		for j := 0; j < c; j++ {
			fmt.Printf(formatString, m.At(i, j))
		}
		fmt.Printf("\n")
	}
}

func generatePopulation(popSize, numQueens int) *mat64.Dense {
	m := mat64.NewDense(popSize, numQueens, nil)
	for i := 0; i < popSize; i++ {
		for j := 0; j < numQueens; j++ {
			m.Set(i, j, float64(rand.Intn(numQueens)))
		}
	}
	return m
}

func fitness(m *mat64.Vector) float64 {
	n, _ := m.Dims()
	attacks := 0
	for i := 0; i < n-1; i++ {
		queen := int(m.At(i, 0))
		for j := i + 1; j < n; j++ {
			other := int(m.At(j, 0))

			if queen == other || queen+i-j == other || queen-i+j == other {
				attacks++
			}
		}
	}
	return 1 - float64(attacks)*2/float64(n-1)/float64(n)
}

func fitnessVector(m *mat64.Dense) []float64 {
	n, _ := m.Dims()
	v := make([]float64, n)
	for i := 0; i < n; i++ {
		v[i] = fitness(m.RowView(i))
	}
	return v
}

func crossover(pop *mat64.Dense, mutationRate float64, fitvec []float64) *mat64.Dense {
	popSize, numQueens := pop.Dims()
	newPop := mat64.NewDense(popSize, numQueens, nil)

	// If the fitnessVector was not provided, calculate it
	if fitvec == nil {
		fitvec = fitnessVector(pop)
	}

	// Distribution from which the index of a parent is taken
	sampler := distuv.NewCategorical(fitvec, nil)

	for i := 0; i < popSize; i++ {
		// Get parents and produce child
		father := int(sampler.Rand())
		mother := int(sampler.Rand())
		crossoverPoint := rand.Intn(numQueens)
		for k := 0; k < crossoverPoint; k++ {
			newPop.Set(i, k, pop.At(father, k))
		}
		for k := crossoverPoint; k < numQueens; k++ {
			newPop.Set(i, k, pop.At(mother, k))
		}

		// Mutation
		if rand.Float64() < mutationRate {
			mutationIndex := rand.Intn(numQueens)
			newValue := rand.Intn(numQueens)
			newPop.Set(i, mutationIndex, float64(newValue))
		}
	}

	return newPop
}

func getMaxInfo(v []float64) (int, float64) {
	n := len(v)
	currentMax := -1.0
	currentIndex := 0

	for i := 0; i < n; i++ {
		if v[i] > currentMax {
			currentMax = v[i]
			currentIndex = i
		}
	}

	return currentIndex, currentMax
}

func main() {
	popSize := 1024
	numQueens := 10
	mutationRate := 0.05

	fmt.Println("Initializing...")

	pop := generatePopulation(popSize, numQueens)
	fit := fitnessVector(pop)
	mIdx, mVal := getMaxInfo(fit)
	gen := 0

	fmt.Println("Evolving...")

	for mVal < 0.99 {
		pop = crossover(pop, mutationRate, fit)
		fit = fitnessVector(pop)
		mIdx, mVal = getMaxInfo(fit)
		gen++
	}

	fmt.Println("Evolution ended.")
	fmt.Printf("Generation: %d\nFitness value: %f\n", gen, mVal)
	printMatrix(pop.RowView(mIdx).T(), 3, 0)
}
<commit_msg>Improve result<commit_after>package main

import (
	"fmt"
	"math/rand"
	"time"

	"github.com/gonum/matrix/mat64"
	"github.com/gonum/stat/distuv"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

func printMatrix(m mat64.Matrix, cols, decs int) {
	formatString := fmt.Sprintf("%%%d.%df", cols, decs)
	r, c := m.Dims()
	for i := 0; i < r; i++ {
		for j := 0; j < c; j++ {
			fmt.Printf(formatString, m.At(i, j))
		}
		fmt.Printf("\n")
	}
}

func generatePopulation(popSize, numQueens int) *mat64.Dense {
	m := mat64.NewDense(popSize, numQueens, nil)
	for i := 0; i < popSize; i++ {
		for j := 0; j < numQueens; j++ {
			m.Set(i, j, float64(rand.Intn(numQueens)))
		}
	}
	return m
}

func fitness(m *mat64.Vector) float64 {
	n, _ := m.Dims()
	attacks := 0
	for i := 0; i < n-1; i++ {
		queen := int(m.At(i, 0))
		for j := i + 1; j < n; j++ {
			other := int(m.At(j, 0))

			if queen == other || queen+i-j == other || queen-i+j == other {
				attacks++
			}
		}
	}
	return 1 - float64(attacks)*2/float64(n-1)/float64(n)
}

func fitnessVector(m *mat64.Dense) []float64 {
	n, _ := m.Dims()
	v := make([]float64, n)
	for i := 0; i < n; i++ {
		v[i] = fitness(m.RowView(i))
	}
	return v
}

func crossover(pop *mat64.Dense, mutationRate float64, fitvec []float64) *mat64.Dense {
	popSize, numQueens := pop.Dims()
	newPop := mat64.NewDense(popSize, numQueens, nil)

	// If the fitnessVector was not provided, calculate it
	if fitvec == nil {
		fitvec = fitnessVector(pop)
	}

	// Distribution from which the index of a parent is taken
	sampler := distuv.NewCategorical(fitvec, nil)

	for i := 0; i < popSize; i++ {
		// Get parents and produce child
		father := int(sampler.Rand())
		mother := int(sampler.Rand())
		crossoverPoint := rand.Intn(numQueens)
		for k := 0; k < crossoverPoint; k++ {
			newPop.Set(i, k, pop.At(father, k))
		}
		for k := crossoverPoint; k < numQueens; k++ {
			newPop.Set(i, k, pop.At(mother, k))
		}

		// Mutation
		if rand.Float64() < mutationRate {
			mutationIndex := rand.Intn(numQueens)
			newValue := rand.Intn(numQueens)
			newPop.Set(i, mutationIndex, float64(newValue))
		}
	}

	return newPop
}

func getMaxInfo(v []float64) (int, float64) {
	n := len(v)
	currentMax := -1.0
	currentIndex := 0

	for i := 0; i < n; i++ {
		if v[i] > currentMax {
			currentMax = v[i]
			currentIndex = i
		}
	}

	return currentIndex, currentMax
}

func main() {
	popSize := 1024
	numQueens := 10
	mutationRate := 0.05

	fmt.Println("Initializing...")

	pop := generatePopulation(popSize, numQueens)
	fit := fitnessVector(pop)
	mIdx, mVal := getMaxInfo(fit)
	gen := 0

	fmt.Println("Evolving...")

	for mVal < 0.99 {
		pop = crossover(pop, mutationRate, fit)
		fit = fitnessVector(pop)
		mIdx, mVal = getMaxInfo(fit)
		gen++
	}

	fmt.Println("Evolution ended.")
	fmt.Printf("Generation: %d\nFitness score: %3.0f%%\n", gen, 100*mVal)
	printMatrix(pop.RowView(mIdx).T(), 3, 0)
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// HTTP version of the cross machine router
// Fetch a list of peers (via an etcd store, DHT, or static list), divvy them
// up into buckets, proxy the update to the servers, stopping once you've
// gotten a successful return.
// PROS:
//  Very simple to implement
//  hosts can autoannounce
//  no AWS dependencies
// CONS:
//  fair bit of cross traffic (try to minimize)

// Obviously a PubSub would be better, but might require more server
// state management (because of duplicate messages)
package simplepush

import (
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	capn "github.com/glycerine/go-capnproto"
)

var (
	ErrNoLocator       = errors.New("Discovery service not configured")
	ErrInvalidRoutable = errors.New("Malformed routable")
)

type RouterConfig struct {
	// BucketSize is the maximum number of contacts to probe at once. The router
	// will defer requests until all nodes in a bucket have responded. Defaults
	// to 10 contacts.
	BucketSize int `toml:"bucket_size" env:"bucket_size"`

	// PoolSize is the number of goroutines to spawn for routing messages.
	// Defaults to 30.
	PoolSize int `toml:"pool_size" env:"pool_size"`

	// Ctimeout is the maximum amount of time that the router's rclient should
	// should wait for a dial to succeed. Defaults to 3 seconds.
	Ctimeout string

	// Rwtimeout is the maximum amount of time that the router should wait for an
	// HTTP request to complete. Defaults to 3 seconds.
	Rwtimeout string

	// DefaultHost is the default hostname of the proxy endpoint. No default
	// value; overrides simplepush.Application.Hostname() if specified.
	DefaultHost string `toml:"default_host" env:"default_host"`

	// Listener specifies the address and port, maximum connections, TCP
	// keep-alive period, and certificate information for the routing listener.
	Listener ListenerConfig
}

// Router proxies incoming updates to the Simple Push server ("contact") that
// currently maintains a WebSocket connection to the target device.
type Router struct {
	locator     Locator
	listener    net.Listener
	logger      *SimpleLogger
	metrics     *Metrics
	ctimeout    time.Duration
	rwtimeout   time.Duration
	bucketSize  int
	poolSize    int
	url         string
	runs        chan func()
	rclient     *http.Client
	closeWait   sync.WaitGroup
	isClosed    bool
	closeSignal chan bool
	closeLock   sync.Mutex
	lastErr     error
}

func NewRouter() *Router {
	return &Router{
		runs:        make(chan func()),
		closeSignal: make(chan bool),
	}
}

func (*Router) ConfigStruct() interface{} {
	return &RouterConfig{
		BucketSize: 10,
		PoolSize:   30,
		Ctimeout:   "3s",
		Rwtimeout:  "3s",
		Listener: ListenerConfig{
			Addr:            ":3000",
			MaxConns:        1000,
			KeepAlivePeriod: "3m",
		},
	}
}

func (r *Router) Init(app *Application, config interface{}) (err error) {
	conf := config.(*RouterConfig)
	r.logger = app.Logger()
	r.metrics = app.Metrics()

	if r.ctimeout, err = time.ParseDuration(conf.Ctimeout); err != nil {
		r.logger.Alert("router", "Could not parse ctimeout",
			LogFields{"error": err.Error(),
				"ctimeout": conf.Ctimeout})
		return err
	}
	if r.rwtimeout, err = time.ParseDuration(conf.Rwtimeout); err != nil {
		r.logger.Alert("router", "Could not parse rwtimeout",
			LogFields{"error": err.Error(),
				"rwtimeout": conf.Rwtimeout})
		return err
	}

	if r.listener, err = conf.Listener.Listen(); err != nil {
		r.logger.Alert("router", "Could not attach listener",
			LogFields{"error": err.Error()})
		return err
	}
	var scheme string
	if conf.Listener.UseTLS() {
		scheme = "https"
	} else {
		scheme = "http"
	}
	host := conf.DefaultHost
	if len(host) == 0 {
		host = app.Hostname()
	}
	addr := r.listener.Addr().(*net.TCPAddr)
	if len(host) == 0 {
		host = addr.IP.String()
	}
	r.url = CanonicalURL(scheme, host, addr.Port)

	r.bucketSize = conf.BucketSize
	r.poolSize = conf.PoolSize

	r.rclient = &http.Client{
		Transport: &http.Transport{
			Dial: TimeoutDialer(r.ctimeout, r.rwtimeout),
			ResponseHeaderTimeout: r.rwtimeout,
			TLSClientConfig:       new(tls.Config),
		},
	}

	r.closeWait.Add(r.poolSize)
	for i := 0; i < r.poolSize; i++ {
		go r.runLoop()
	}

	return nil
}

func (r *Router) SetLocator(locator Locator) error {
	r.locator = locator
	return nil
}

func (r *Router) Locator() Locator {
	return r.locator
}

func (r *Router) Listener() net.Listener {
	return r.listener
}

func (r *Router) URL() string {
	return r.url
}

func (r *Router) Close() (err error) {
	r.closeLock.Lock()
	err = r.lastErr
	if r.isClosed {
		r.closeLock.Unlock()
		return err
	}
	r.isClosed = true
	close(r.closeSignal)
	if locator := r.Locator(); locator != nil {
		r.lastErr = locator.Close()
	}
	if err := r.listener.Close(); err != nil {
		r.lastErr = err
	}
	r.closeLock.Unlock()
	r.closeWait.Wait()
	return err
}

// Route routes an update packet to the correct server.
func (r *Router) Route(cancelSignal <-chan bool, uaid, chid string, version int64, sentAt time.Time, logID string) (err error) {
	startTime := time.Now()
	locator := r.Locator()
	if locator == nil {
		if r.logger.ShouldLog(ERROR) {
			r.logger.Error("router", "No discovery service set; unable to route message",
				LogFields{"rid": logID, "uaid": uaid, "chid": chid})
		}
		r.metrics.Increment("router.broadcast.error")
		return ErrNoLocator
	}
	segment := capn.NewBuffer(nil)
	routable := NewRootRoutable(segment)
	routable.SetChannelID(chid)
	routable.SetVersion(version)
	routable.SetTime(sentAt.UnixNano())
	contacts, err := locator.Contacts(uaid)
	if err != nil {
		if r.logger.ShouldLog(ERROR) {
			r.logger.Error("router", "Could not query discovery service for contacts",
				LogFields{"rid": logID, "error": err.Error()})
		}
		r.metrics.Increment("router.broadcast.error")
		return err
	}
	if r.logger.ShouldLog(DEBUG) {
		r.logger.Debug("router", "Fetched contact list from discovery service",
			LogFields{"rid": logID, "servers": strings.Join(contacts, ", ")})
	}
	if r.logger.ShouldLog(INFO) {
		r.logger.Info("router", "Sending push...", LogFields{
			"rid":     logID,
			"uaid":    uaid,
			"chid":    chid,
			"version": strconv.FormatInt(version, 10),
			"time":    strconv.FormatInt(sentAt.UnixNano(), 10)})
	}
	ok, err := r.notifyAll(cancelSignal, contacts, uaid, segment, logID)
	endTime := time.Now()
	if err != nil {
		if r.logger.ShouldLog(WARNING) {
			r.logger.Warn("router", "Could not post to server",
				LogFields{"rid": logID, "error": err.Error()})
		}
		r.metrics.Increment("router.broadcast.error")
		return err
	}
	var counterName, timerName string
	if ok {
		counterName = "router.broadcast.hit"
		timerName = "updates.routed.hits"
	} else {
		counterName = "router.broadcast.miss"
		timerName = "updates.routed.misses"
	}
	r.metrics.Increment(counterName)
	r.metrics.Timer(timerName, endTime.Sub(sentAt))
	r.metrics.Timer("router.handled", endTime.Sub(startTime))
	return nil
}

// notifyAll partitions a slice of contacts into buckets, then broadcasts an
// update to each bucket.
func (r *Router) notifyAll(cancelSignal <-chan bool, contacts []string,
	uaid string, segment *capn.Segment, logID string) (ok bool, err error) {

	for fromIndex := 0; !ok && fromIndex < len(contacts); {
		toIndex := fromIndex + r.bucketSize
		if toIndex > len(contacts) {
			toIndex = len(contacts)
		}
		if ok, err = r.notifyBucket(cancelSignal, contacts[fromIndex:toIndex],
			uaid, segment, logID); err != nil {
			break
		}
		fromIndex += toIndex
	}
	return
}

// notifyBucket routes a message to all contacts in a bucket, returning as soon
// as a contact accepts the update.
func (r *Router) notifyBucket(cancelSignal <-chan bool, contacts []string,
	uaid string, segment *capn.Segment, logID string) (ok bool, err error) {

	result, stop := make(chan bool), make(chan struct{})
	defer close(stop)
	timeout := r.ctimeout + r.rwtimeout + 1*time.Second
	timer := time.NewTimer(timeout)
	for _, contact := range contacts {
		url := fmt.Sprintf("%s/route/%s", contact, uaid)
		notify := func() {
			r.notifyContact(result, stop, url, segment, logID)
		}
		select {
		case <-r.closeSignal:
			return false, io.EOF
		case <-cancelSignal:
			return false, nil
		case ok = <-result:
			return ok, nil
		case <-timer.C:
			return false, nil
		case r.runs <- notify:
		}
	}
	timer.Reset(timeout)
	select {
	case ok = <-r.closeSignal:
		return false, io.EOF
	case <-cancelSignal:
	case ok = <-result:
	case <-timer.C:
	}
	return ok, nil
}

// notifyContact routes a message to a single contact.
func (r *Router) notifyContact(result chan<- bool, stop <-chan struct{},
	url string, segment *capn.Segment, logID string) {

	reader, writer := io.Pipe()
	go pipeTo(writer, segment)
	req, err := http.NewRequest("PUT", url, reader)
	if err != nil {
		if r.logger.ShouldLog(ERROR) {
			r.logger.Error("router", "Router request failed",
				LogFields{"rid": logID, "error": err.Error()})
		}
		return
	}
	req.Header.Set(HeaderID, logID)
	if r.logger.ShouldLog(DEBUG) {
		r.logger.Debug("router", "Sending request",
			LogFields{"rid": logID, "url": url})
	}
	req.Header.Add("Content-Type", "application/json")
	resp, err := r.rclient.Do(req)
	if err != nil {
		if r.logger.ShouldLog(ERROR) {
			r.logger.Error("router", "Router send failed",
				LogFields{"rid": logID, "error": err.Error()})
		}
		return
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		if r.logger.ShouldLog(DEBUG) {
			r.logger.Debug("router", "Denied",
				LogFields{"rid": logID, "url": url})
		}
		return
	}
	if r.logger.ShouldLog(INFO) {
		r.logger.Info("router", "Server accepted",
			LogFields{"rid": logID, "url": url})
	}
	select {
	case <-stop:
	case result <- true:
	case <-time.After(1 * time.Second):
	}
}

func (r *Router) runLoop() {
	defer r.closeWait.Done()
	for ok := true; ok; {
		select {
		case ok = <-r.closeSignal:
		case run := <-r.runs:
			run()
		}
	}
}

func pipeTo(dest io.WriteCloser, src io.WriterTo) (err error) {
	if _, err = src.WriteTo(dest); err != nil {
		return
	}
	return dest.Close()
}
<commit_msg>Router: Replace `io.Pipe()` with a buffer.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// HTTP version of the cross machine router
// Fetch a list of peers (via an etcd store, DHT, or static list), divvy them
// up into buckets, proxy the update to the servers, stopping once you've
// gotten a successful return.
// PROS:
//  Very simple to implement
//  hosts can autoannounce
//  no AWS dependencies
// CONS:
//  fair bit of cross traffic (try to minimize)

// Obviously a PubSub would be better, but might require more server
// state management (because of duplicate messages)
package simplepush

import (
	"bytes"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	capn "github.com/glycerine/go-capnproto"
)

var (
	ErrNoLocator       = errors.New("Discovery service not configured")
	ErrInvalidRoutable = errors.New("Malformed routable")
)

type RouterConfig struct {
	// BucketSize is the maximum number of contacts to probe at once. The router
	// will defer requests until all nodes in a bucket have responded. Defaults
	// to 10 contacts.
	BucketSize int `toml:"bucket_size" env:"bucket_size"`

	// PoolSize is the number of goroutines to spawn for routing messages.
	// Defaults to 30.
	PoolSize int `toml:"pool_size" env:"pool_size"`

	// Ctimeout is the maximum amount of time that the router's rclient should
	// should wait for a dial to succeed. Defaults to 3 seconds.
	Ctimeout string

	// Rwtimeout is the maximum amount of time that the router should wait for an
	// HTTP request to complete. Defaults to 3 seconds.
	Rwtimeout string

	// DefaultHost is the default hostname of the proxy endpoint. No default
	// value; overrides simplepush.Application.Hostname() if specified.
	DefaultHost string `toml:"default_host" env:"default_host"`

	// Listener specifies the address and port, maximum connections, TCP
	// keep-alive period, and certificate information for the routing listener.
	Listener ListenerConfig
}

// Router proxies incoming updates to the Simple Push server ("contact") that
// currently maintains a WebSocket connection to the target device.
type Router struct {
	locator     Locator
	listener    net.Listener
	logger      *SimpleLogger
	metrics     *Metrics
	ctimeout    time.Duration
	rwtimeout   time.Duration
	bucketSize  int
	poolSize    int
	url         string
	runs        chan func()
	rclient     *http.Client
	closeWait   sync.WaitGroup
	isClosed    bool
	closeSignal chan bool
	closeLock   sync.Mutex
	lastErr     error
}

func NewRouter() *Router {
	return &Router{
		runs:        make(chan func()),
		closeSignal: make(chan bool),
	}
}

func (*Router) ConfigStruct() interface{} {
	return &RouterConfig{
		BucketSize: 10,
		PoolSize:   30,
		Ctimeout:   "3s",
		Rwtimeout:  "3s",
		Listener: ListenerConfig{
			Addr:            ":3000",
			MaxConns:        1000,
			KeepAlivePeriod: "3m",
		},
	}
}

func (r *Router) Init(app *Application, config interface{}) (err error) {
	conf := config.(*RouterConfig)
	r.logger = app.Logger()
	r.metrics = app.Metrics()

	if r.ctimeout, err = time.ParseDuration(conf.Ctimeout); err != nil {
		r.logger.Alert("router", "Could not parse ctimeout",
			LogFields{"error": err.Error(),
				"ctimeout": conf.Ctimeout})
		return err
	}
	if r.rwtimeout, err = time.ParseDuration(conf.Rwtimeout); err != nil {
		r.logger.Alert("router", "Could not parse rwtimeout",
			LogFields{"error": err.Error(),
				"rwtimeout": conf.Rwtimeout})
		return err
	}

	if r.listener, err = conf.Listener.Listen(); err != nil {
		r.logger.Alert("router", "Could not attach listener",
			LogFields{"error": err.Error()})
		return err
	}
	var scheme string
	if conf.Listener.UseTLS() {
		scheme = "https"
	} else {
		scheme = "http"
	}
	host := conf.DefaultHost
	if len(host) == 0 {
		host = app.Hostname()
	}
	addr := r.listener.Addr().(*net.TCPAddr)
	if len(host) == 0 {
		host = addr.IP.String()
	}
	r.url = CanonicalURL(scheme, host, addr.Port)

	r.bucketSize = conf.BucketSize
	r.poolSize = conf.PoolSize

	r.rclient = &http.Client{
		Transport: &http.Transport{
			Dial: TimeoutDialer(r.ctimeout, r.rwtimeout),
			ResponseHeaderTimeout: r.rwtimeout,
			TLSClientConfig:       new(tls.Config),
		},
	}

	r.closeWait.Add(r.poolSize)
	for i := 0; i < r.poolSize; i++ {
		go r.runLoop()
	}

	return nil
}

func (r *Router) SetLocator(locator Locator) error {
	r.locator = locator
	return nil
}

func (r *Router) Locator() Locator {
	return r.locator
}

func (r *Router) Listener() net.Listener {
	return r.listener
}

func (r *Router) URL() string {
	return r.url
}

func (r *Router) Close() (err error) {
	r.closeLock.Lock()
	err = r.lastErr
	if r.isClosed {
		r.closeLock.Unlock()
		return err
	}
	r.isClosed = true
	close(r.closeSignal)
	if locator := r.Locator(); locator != nil {
		r.lastErr = locator.Close()
	}
	if err := r.listener.Close(); err != nil {
		r.lastErr = err
	}
	r.closeLock.Unlock()
	r.closeWait.Wait()
	return err
}

// Route routes an update packet to the correct server.
func (r *Router) Route(cancelSignal <-chan bool, uaid, chid string, version int64, sentAt time.Time, logID string) (err error) {
	startTime := time.Now()
	locator := r.Locator()
	if locator == nil {
		if r.logger.ShouldLog(ERROR) {
			r.logger.Error("router", "No discovery service set; unable to route message",
				LogFields{"rid": logID, "uaid": uaid, "chid": chid})
		}
		r.metrics.Increment("router.broadcast.error")
		return ErrNoLocator
	}
	segment := capn.NewBuffer(nil)
	routable := NewRootRoutable(segment)
	routable.SetChannelID(chid)
	routable.SetVersion(version)
	routable.SetTime(sentAt.UnixNano())
	contacts, err := locator.Contacts(uaid)
	if err != nil {
		if r.logger.ShouldLog(ERROR) {
			r.logger.Error("router", "Could not query discovery service for contacts",
				LogFields{"rid": logID, "error": err.Error()})
		}
		r.metrics.Increment("router.broadcast.error")
		return err
	}
	if r.logger.ShouldLog(DEBUG) {
		r.logger.Debug("router", "Fetched contact list from discovery service",
			LogFields{"rid": logID, "servers": strings.Join(contacts, ", ")})
	}
	if r.logger.ShouldLog(INFO) {
		r.logger.Info("router", "Sending push...", LogFields{
			"rid":     logID,
			"uaid":    uaid,
			"chid":    chid,
			"version": strconv.FormatInt(version, 10),
			"time":    strconv.FormatInt(sentAt.UnixNano(), 10)})
	}
	ok, err := r.notifyAll(cancelSignal, contacts, uaid, segment, logID)
	endTime := time.Now()
	if err != nil {
		if r.logger.ShouldLog(WARNING) {
			r.logger.Warn("router", "Could not post to server",
				LogFields{"rid": logID, "error": err.Error()})
		}
		r.metrics.Increment("router.broadcast.error")
		return err
	}
	var counterName, timerName string
	if ok {
		counterName = "router.broadcast.hit"
		timerName = "updates.routed.hits"
	} else {
		counterName = "router.broadcast.miss"
		timerName = "updates.routed.misses"
	}
	r.metrics.Increment(counterName)
	r.metrics.Timer(timerName, endTime.Sub(sentAt))
	r.metrics.Timer("router.handled", endTime.Sub(startTime))
	return nil
}

// notifyAll partitions a slice of contacts into buckets, then broadcasts an
// update to each bucket.
func (r *Router) notifyAll(cancelSignal <-chan bool, contacts []string,
	uaid string, segment *capn.Segment, logID string) (ok bool, err error) {

	for fromIndex := 0; !ok && fromIndex < len(contacts); {
		toIndex := fromIndex + r.bucketSize
		if toIndex > len(contacts) {
			toIndex = len(contacts)
		}
		if ok, err = r.notifyBucket(cancelSignal, contacts[fromIndex:toIndex],
			uaid, segment, logID); err != nil {
			break
		}
		fromIndex += toIndex
	}
	return
}

// notifyBucket routes a message to all contacts in a bucket, returning as soon
// as a contact accepts the update.
func (r *Router) notifyBucket(cancelSignal <-chan bool, contacts []string,
	uaid string, segment *capn.Segment, logID string) (ok bool, err error) {

	result, stop := make(chan bool), make(chan struct{})
	defer close(stop)
	timeout := r.ctimeout + r.rwtimeout + 1*time.Second
	timer := time.NewTimer(timeout)
	for _, contact := range contacts {
		url := fmt.Sprintf("%s/route/%s", contact, uaid)
		notify := func() {
			r.notifyContact(result, stop, url, segment, logID)
		}
		select {
		case <-r.closeSignal:
			return false, io.EOF
		case <-cancelSignal:
			return false, nil
		case ok = <-result:
			return ok, nil
		case <-timer.C:
			return false, nil
		case r.runs <- notify:
		}
	}
	timer.Reset(timeout)
	select {
	case ok = <-r.closeSignal:
		return false, io.EOF
	case <-cancelSignal:
	case ok = <-result:
	case <-timer.C:
	}
	return ok, nil
}

// notifyContact routes a message to a single contact.
func (r *Router) notifyContact(result chan<- bool, stop <-chan struct{},
	url string, segment *capn.Segment, logID string) {

	body := new(bytes.Buffer)
	if _, err := segment.WriteTo(body); err != nil {
		if r.logger.ShouldLog(ERROR) {
			r.logger.Error("router", "Error buffering router request",
				LogFields{"rid": logID, "error": err.Error()})
		}
		return
	}
	req, err := http.NewRequest("PUT", url, body)
	if err != nil {
		if r.logger.ShouldLog(ERROR) {
			r.logger.Error("router", "Router request failed",
				LogFields{"rid": logID, "error": err.Error()})
		}
		return
	}
	req.Header.Set(HeaderID, logID)
	if r.logger.ShouldLog(DEBUG) {
		r.logger.Debug("router", "Sending request",
			LogFields{"rid": logID, "url": url})
	}
	req.Header.Add("Content-Type", "application/json")
	resp, err := r.rclient.Do(req)
	if err != nil {
		if r.logger.ShouldLog(ERROR) {
			r.logger.Error("router", "Router send failed",
				LogFields{"rid": logID, "error": err.Error()})
		}
		return
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		if r.logger.ShouldLog(DEBUG) {
			r.logger.Debug("router", "Denied",
				LogFields{"rid": logID, "url": url})
		}
		return
	}
	if r.logger.ShouldLog(INFO) {
		r.logger.Info("router", "Server accepted",
			LogFields{"rid": logID, "url": url})
	}
	select {
	case <-stop:
	case result <- true:
	case <-time.After(1 * time.Second):
	}
}

func (r *Router) runLoop() {
	defer r.closeWait.Done()
	for ok := true; ok; {
		select {
		case ok = <-r.closeSignal:
		case run := <-r.runs:
			run()
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/ebml-go/webm"
	"code.google.com/p/portaudio-go/portaudio"
	"flag"
	gl "github.com/chsc/gogl/gl21"
	"github.com/jteeuwen/glfw"
	"log"
	"math"
	"os"
	"runtime"
	"time"
)

var (
	in         = flag.String("i", "", "Input file")
	unsync     = flag.Bool("u", false, "Unsynchronized display")
	notc       = flag.Bool("t", false, "Ignore timecodes")
	blend      = flag.Bool("b", false, "Blend between images")
	fullscreen = flag.Bool("f", false, "Fullscreen mode")
	justaudio  = flag.Bool("a", false, "Just audio")
	justvideo  = flag.Bool("v", false, "Just video")
)

var ntex int

const vss = `
void main() {
  gl_TexCoord[0] = gl_MultiTexCoord0;
  gl_Position = ftransform();
}
`

const ycbcr2rgb = `
const mat3 ycbcr2rgb = mat3(
                          1.164, 0, 1.596,
                          1.164, -0.392, -0.813,
                          1.164, 2.017, 0.0
                          );
const float ysub = 0.0625;
vec3 ycbcr2rgb(vec3 c) {
   vec3 ycbcr = vec3(c.x - ysub, c.y - 0.5, c.z - 0.5);
   return ycbcr * ycbcr2rgb;
}
`

const fss = ycbcr2rgb + `
uniform sampler2D yt1;
uniform sampler2D cbt1;
uniform sampler2D crt1;

void main() {
   vec3 c = vec3(texture2D(yt1, gl_TexCoord[0].st).r,
                 texture2D(cbt1, gl_TexCoord[0].st).r,
                 texture2D(crt1, gl_TexCoord[0].st).r);
   gl_FragColor = vec4(ycbcr2rgb(c), 1.0);
}
`
const bfss = ycbcr2rgb + `
uniform sampler2D yt1;
uniform sampler2D cbt1;
uniform sampler2D crt1;
uniform sampler2D yt0;
uniform sampler2D cbt0;
uniform sampler2D crt0;
uniform float factor;

void main() {
   vec3 c0 = vec3(texture2D(yt0, gl_TexCoord[0].st).r,
                  texture2D(cbt0, gl_TexCoord[0].st).r,
                  texture2D(crt0, gl_TexCoord[0].st).r);
   vec3 c1 = vec3(texture2D(yt1, gl_TexCoord[0].st).r,
                  texture2D(cbt1, gl_TexCoord[0].st).r,
                  texture2D(crt1, gl_TexCoord[0].st).r);
   gl_FragColor = vec4(ycbcr2rgb(mix(c0, c1, factor)), 1);
}
`

func texinit(id int) {
	gl.BindTexture(gl.TEXTURE_2D, gl.Uint(id))
	gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
	gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
	gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
	gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
}

func shinit() gl.Int {
	vs := loadShader(gl.VERTEX_SHADER, vss)
	var sfs string
	if *blend {
		sfs = bfss
	} else {
		sfs = fss
	}
	fs := loadShader(gl.FRAGMENT_SHADER, sfs)
	prg := gl.CreateProgram()
	gl.AttachShader(prg, vs)
	gl.AttachShader(prg, fs)
	gl.LinkProgram(prg)
	var l int
	if *blend {
		l = 6
	} else {
		l = 3
	}
	gl.UseProgram(prg)
	names := []string{"yt1", "cbt1", "crt1", "yt0", "cbt0", "crt0"}
	for i := 0; i < l; i++ {
		loc := gl.GetUniformLocation(prg, gl.GLString(names[i]))
		gl.Uniform1i(loc, gl.Int(i))
	}
	return gl.GetUniformLocation(prg, gl.GLString("factor"))
}

func upload(id gl.Uint, data []byte, stride int, w int, h int) {
	gl.BindTexture(gl.TEXTURE_2D, id)
	gl.PixelStorei(gl.UNPACK_ROW_LENGTH, gl.Int(stride))
	gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1)
	gl.TexImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, gl.Sizei(w), gl.Sizei(h), 0,
		gl.LUMINANCE, gl.UNSIGNED_BYTE, gl.Pointer(&data[0]))
}

func initquad() {
	ver := []gl.Float{-1, 1, 1, 1, -1, -1, 1, -1}
	gl.BindBuffer(gl.ARRAY_BUFFER, 1)
	gl.BufferData(gl.ARRAY_BUFFER, gl.Sizeiptr(4*len(ver)),
		gl.Pointer(&ver[0]), gl.STATIC_DRAW)
	gl.VertexPointer(2, gl.FLOAT, 0, nil)
	tex := []gl.Float{0, 0, 1, 0, 0, 1, 1, 1}
	gl.BindBuffer(gl.ARRAY_BUFFER, 2)
	gl.BufferData(gl.ARRAY_BUFFER, gl.Sizeiptr(4*len(tex)),
		gl.Pointer(&tex[0]), gl.STATIC_DRAW)
	gl.TexCoordPointer(2, gl.FLOAT, 0, nil)
	gl.EnableClientState(gl.VERTEX_ARRAY)
	gl.EnableClientState(gl.TEXTURE_COORD_ARRAY)
}

func loadShader(shtype gl.Enum, src string) gl.Uint {
	sh := gl.CreateShader(shtype)
	gsrc := gl.GLString(src)
	gl.ShaderSource(sh, 1, &gsrc, nil)
	gl.CompileShader(sh)
	return sh
}

func factor(t time.Time, tc0 time.Time, tc1 time.Time) gl.Float {
	num := t.Sub(tc0)
	den := tc1.Sub(tc0)
	res := num.Seconds() / den.Seconds()
	res = math.Max(res, 0)
	res = math.Min(res, 1)
	return gl.Float(res)
}

var steps = uint(0xffffffff)
var seek = webm.BadTC
var duration = webm.BadTC
var lasttc = webm.BadTC
var fduration = webm.BadTC

func xseek(x int) {
	w, _ := glfw.WindowSize()
	factor := float64(x) / float64(w)
	seek = time.Duration(float64(duration) * factor)
}

func mphandler(x, y int) {
	if glfw.KeyPress == glfw.MouseButton(glfw.MouseLeft) {
		xseek(x)
	}
}

func mbhandler(button, state int) {
	if state == glfw.KeyPress {
		x, _ := glfw.MousePos()
		xseek(x)
	}
}

func khandler(key, state int) {
	if state == glfw.KeyRelease {
		switch key {
		case 'P':
			steps = 0
		case 'R':
			steps = 0xffffffff
			seek = lasttc
		case 'F':
			steps = 1
			seek = lasttc + fduration
		case 'B':
			steps = 1
			seek = lasttc - fduration
		}
	}
}

func vpresent(wchan <-chan webm.Frame, reader *webm.Reader) {
	if *blend {
		ntex = 6
	} else {
		ntex = 3
	}
	img := <-wchan
	w := img.Rect.Dx()
	h := img.Rect.Dy()
	gl.Init()
	glfw.Init()
	defer glfw.Terminate()
	mode := glfw.Windowed
	ww := w
	wh := h
	if *fullscreen {
		mode = glfw.Fullscreen
		ww = 1440
		wh = 900
	}
	glfw.OpenWindow(ww, wh, 0, 0, 0, 0, 0, 0, mode)
	glfw.SetKeyCallback(khandler)
	glfw.SetMouseButtonCallback(mbhandler)
	glfw.SetMousePosCallback(mphandler)
	defer glfw.CloseWindow()
	glfw.SetWindowSizeCallback(func(ww, wh int) {
		oaspect := float64(w) / float64(h)
		haspect := float64(ww) / float64(wh)
		vaspect := float64(wh) / float64(ww)
		var scx, scy float64
		if oaspect > haspect {
			scx = 1
			scy = haspect / oaspect
		} else {
			scx = vaspect * oaspect
			scy = 1
		}
		gl.Viewport(0, 0, gl.Sizei(ww), gl.Sizei(wh))
		gl.LoadIdentity()
		gl.Scaled(gl.Double(scx), gl.Double(scy), 1)
	})
	if !*unsync {
		glfw.SetSwapInterval(1)
	}
	glfw.SetWindowTitle(*in)
	for i := 0; i < ntex; i++ {
		texinit(i + 1)
	}
	factorloc := shinit()
	initquad()
	gl.Enable(gl.TEXTURE_2D)
	tbase := time.Now()
	pimg := img

	flushing := false
	for glfw.WindowParam(glfw.Opened) == 1 {
		if seek != webm.BadTC {
			reader.Seek(seek)
			seek = webm.BadTC
			flushing = true
		}

		gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
		t := time.Now()
		if flushing || (steps > 0 && (*notc || t.After(tbase.Add(img.Timecode)))) {
			pimg = img
			var ok bool
			var nimg webm.Frame
			nimg, ok = <-wchan
			if !ok {
				return
			}
			if nimg.Timecode == pimg.Timecode {
				log.Println("same timecode", img.Timecode)
			}
			if nimg.Rebase {
				tbase = time.Now().Add(-nimg.Timecode)
				flushing = false
			}
			if !flushing {
				steps--
				img = nimg
			}
		}
		lasttc = img.Timecode

		gl.ActiveTexture(gl.TEXTURE0)
		upload(1, img.Y, img.YStride, w, h)
		gl.ActiveTexture(gl.TEXTURE1)
		upload(2, img.Cb, img.CStride, w/2, h/2)
		gl.ActiveTexture(gl.TEXTURE2)
		upload(3, img.Cr, img.CStride, w/2, h/2)
		if *blend {
			gl.Uniform1f(factorloc, factor(t,
				tbase.Add(pimg.Timecode),
				tbase.Add(img.Timecode)))
			gl.ActiveTexture(gl.TEXTURE3)
			upload(4, pimg.Y, pimg.YStride, w, h)
			gl.ActiveTexture(gl.TEXTURE4)
			upload(5, pimg.Cb, pimg.CStride, w/2, h/2)
			gl.ActiveTexture(gl.TEXTURE5)
			upload(6, pimg.Cr, pimg.CStride, w/2, h/2)
		}
		gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
		runtime.GC()
		glfw.SwapBuffers()
	}
}

type AudioWriter struct {
	ch       <-chan webm.Samples
	channels int
	active   bool
	sofar    int
	curr     webm.Samples
}

func (aw *AudioWriter) ProcessAudio(in, out []float32) {
	for sent, lo := 0, len(out); sent < lo; {
		if aw.sofar == len(aw.curr.Data) {
			aw.curr, aw.active = <-aw.ch
			aw.sofar = 0
			//log.Println("timecode", aw.curr.Timecode)
		}
		s := copy(out[sent:], aw.curr.Data[aw.sofar:])
		sent += s
		aw.sofar += s
	}
}

func apresent(wchan <-chan webm.Samples, audio *webm.Audio) {
	chk := func(err error) {
		if err != nil {
			panic(err)
		}
	}
	channels := int(audio.Channels)
	aw := AudioWriter{ch: wchan, channels: channels, active: true}
	stream, err := portaudio.OpenDefaultStream(0, channels,
		audio.SamplingFrequency, 0, &aw)
	defer stream.Close()
	chk(err)
	chk(stream.Start())
	defer stream.Stop()
	for aw.active {
		time.Sleep(time.Second)
	}
}

func main() {
	flag.Parse()

	var err error
	var wm webm.WebM
	r, err := os.Open(*in)
	defer r.Close()
	if err != nil {
		log.Panic("Unable to open file " + *in)
	}
	reader, err := webm.Parse(r, &wm)
	if err != nil {
		log.Panic("Unable to parse file:", err)
	}
	duration = wm.GetDuration()

	splitter := webm.NewSplitter(reader.Chan)

	var vtrack *webm.TrackEntry
	var vstream *webm.Stream
	if !*justaudio {
		vtrack = wm.FindFirstVideoTrack()
		fduration = vtrack.GetDefaultDuration()
	}
	if vtrack != nil {
		vstream = webm.NewStream(vtrack)
	}
	var astream *webm.Stream
	var atrack *webm.TrackEntry
	if !*justvideo {
		atrack = wm.FindFirstAudioTrack()
	}
	if atrack != nil {
		astream = webm.NewStream(atrack)
	}
	splitter.Split(astream, vstream)
	switch {
	case astream != nil && vstream != nil:
		go apresent(astream.AudioChannel(), &atrack.Audio)
		fallthrough
	case vstream != nil:
		vpresent(vstream.VideoChannel(), reader)
	case astream != nil:
		apresent(astream.AudioChannel(), &atrack.Audio)
	}
}
<commit_msg>Don't seek to negative values<commit_after>package main

import (
	"code.google.com/p/ebml-go/webm"
	"code.google.com/p/portaudio-go/portaudio"
	"flag"
	gl "github.com/chsc/gogl/gl21"
	"github.com/jteeuwen/glfw"
	"log"
	"math"
	"os"
	"runtime"
	"time"
)

var (
	in         = flag.String("i", "", "Input file")
	unsync     = flag.Bool("u", false, "Unsynchronized display")
	notc       = flag.Bool("t", false, "Ignore timecodes")
	blend      = flag.Bool("b", false, "Blend between images")
	fullscreen = flag.Bool("f", false, "Fullscreen mode")
	justaudio  = flag.Bool("a", false, "Just audio")
	justvideo  = flag.Bool("v", false, "Just video")
)

var ntex int

const vss = `
void main() {
  gl_TexCoord[0] = gl_MultiTexCoord0;
  gl_Position = ftransform();
}
`

const ycbcr2rgb = `
const mat3 ycbcr2rgb = mat3(
                          1.164, 0, 1.596,
                          1.164, -0.392, -0.813,
                          1.164, 2.017, 0.0
                          );
const float ysub = 0.0625;
vec3 ycbcr2rgb(vec3 c) {
   vec3 ycbcr = vec3(c.x - ysub, c.y - 0.5, c.z - 0.5);
   return ycbcr * ycbcr2rgb;
}
`

const fss = ycbcr2rgb + `
uniform sampler2D yt1;
uniform sampler2D cbt1;
uniform sampler2D crt1;

void main() {
   vec3 c = vec3(texture2D(yt1, gl_TexCoord[0].st).r,
                 texture2D(cbt1, gl_TexCoord[0].st).r,
                 texture2D(crt1, gl_TexCoord[0].st).r);
   gl_FragColor = vec4(ycbcr2rgb(c), 1.0);
}
`
const bfss = ycbcr2rgb + `
uniform sampler2D yt1;
uniform sampler2D cbt1;
uniform sampler2D crt1;
uniform sampler2D yt0;
uniform sampler2D cbt0;
uniform sampler2D crt0;
uniform float factor;

void main() {
   vec3 c0 = vec3(texture2D(yt0, gl_TexCoord[0].st).r,
                  texture2D(cbt0, gl_TexCoord[0].st).r,
                  texture2D(crt0, gl_TexCoord[0].st).r);
   vec3 c1 = vec3(texture2D(yt1, gl_TexCoord[0].st).r,
                  texture2D(cbt1, gl_TexCoord[0].st).r,
                  texture2D(crt1, gl_TexCoord[0].st).r);
   gl_FragColor = vec4(ycbcr2rgb(mix(c0, c1, factor)), 1);
}
`

func texinit(id int) {
	gl.BindTexture(gl.TEXTURE_2D, gl.Uint(id))
	gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
	gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
	gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
	gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
}

func shinit() gl.Int {
	vs := loadShader(gl.VERTEX_SHADER, vss)
	var sfs string
	if *blend {
		sfs = bfss
	} else {
		sfs = fss
	}
	fs := loadShader(gl.FRAGMENT_SHADER, sfs)
	prg := gl.CreateProgram()
	gl.AttachShader(prg, vs)
	gl.AttachShader(prg, fs)
	gl.LinkProgram(prg)
	var l int
	if *blend {
		l = 6
	} else {
		l = 3
	}
	gl.UseProgram(prg)
	names := []string{"yt1", "cbt1", "crt1", "yt0", "cbt0", "crt0"}
	for i := 0; i < l; i++ {
		loc := gl.GetUniformLocation(prg, gl.GLString(names[i]))
		gl.Uniform1i(loc, gl.Int(i))
	}
	return gl.GetUniformLocation(prg, gl.GLString("factor"))
}

func upload(id gl.Uint, data []byte, stride int, w int, h int) {
	gl.BindTexture(gl.TEXTURE_2D, id)
	gl.PixelStorei(gl.UNPACK_ROW_LENGTH, gl.Int(stride))
	gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1)
	gl.TexImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, gl.Sizei(w), gl.Sizei(h), 0,
		gl.LUMINANCE, gl.UNSIGNED_BYTE, gl.Pointer(&data[0]))
}

func initquad() {
	ver := []gl.Float{-1, 1, 1, 1, -1, -1, 1, -1}
	gl.BindBuffer(gl.ARRAY_BUFFER, 1)
	gl.BufferData(gl.ARRAY_BUFFER, gl.Sizeiptr(4*len(ver)),
		gl.Pointer(&ver[0]), gl.STATIC_DRAW)
	gl.VertexPointer(2, gl.FLOAT, 0, nil)
	tex := []gl.Float{0, 0, 1, 0, 0, 1, 1, 1}
	gl.BindBuffer(gl.ARRAY_BUFFER, 2)
	gl.BufferData(gl.ARRAY_BUFFER, gl.Sizeiptr(4*len(tex)),
		gl.Pointer(&tex[0]), gl.STATIC_DRAW)
	gl.TexCoordPointer(2, gl.FLOAT, 0, nil)
	gl.EnableClientState(gl.VERTEX_ARRAY)
	gl.EnableClientState(gl.TEXTURE_COORD_ARRAY)
}

func loadShader(shtype gl.Enum, src string) gl.Uint {
	sh := gl.CreateShader(shtype)
	gsrc := gl.GLString(src)
	gl.ShaderSource(sh, 1, &gsrc, nil)
	gl.CompileShader(sh)
	return sh
}

func factor(t time.Time, tc0 time.Time, tc1 time.Time) gl.Float {
	num := t.Sub(tc0)
	den := tc1.Sub(tc0)
	res := num.Seconds() / den.Seconds()
	res = math.Max(res, 0)
	res = math.Min(res, 1)
	return gl.Float(res)
}

var steps = uint(0xffffffff)
var seek = webm.BadTC
var duration = webm.BadTC
var lasttc = webm.BadTC
var fduration = webm.BadTC

func tseek(t time.Duration) {
	seek = t
	if seek < 0 {
		seek = 0
	}
}

func xseek(x int) {
	w, _ := glfw.WindowSize()
	factor := float64(x) / float64(w)
	tseek(time.Duration(float64(duration) * factor))
}

func mphandler(x, y int) {
	if glfw.KeyPress == glfw.MouseButton(glfw.MouseLeft) {
		xseek(x)
	}
}

func mbhandler(button, state int) {
	if state == glfw.KeyPress {
		x, _ := glfw.MousePos()
		xseek(x)
	}
}

func khandler(key, state int) {
	if state == glfw.KeyRelease {
		switch key {
		case 'P':
			steps = 0
		case 'R':
			steps = 0xffffffff
			tseek(lasttc)
		case 'F':
			steps = 1
			tseek(lasttc + fduration)
		case 'B':
			steps = 1
			tseek(lasttc - fduration)
		}
	}
}

func vpresent(wchan <-chan webm.Frame, reader *webm.Reader) {
	if *blend {
		ntex = 6
	} else {
		ntex = 3
	}
	img := <-wchan
	w := img.Rect.Dx()
	h := img.Rect.Dy()
	gl.Init()
	glfw.Init()
	defer glfw.Terminate()
	mode := glfw.Windowed
	ww := w
	wh := h
	if *fullscreen {
		mode = glfw.Fullscreen
		ww = 1440
		wh = 900
	}
	glfw.OpenWindow(ww, wh, 0, 0, 0, 0, 0, 0, mode)
	glfw.SetKeyCallback(khandler)
	glfw.SetMouseButtonCallback(mbhandler)
	glfw.SetMousePosCallback(mphandler)
	defer glfw.CloseWindow()
	glfw.SetWindowSizeCallback(func(ww, wh int) {
		oaspect := float64(w) / float64(h)
		haspect := float64(ww) / float64(wh)
		vaspect := float64(wh) / float64(ww)
		var scx, scy float64
		if oaspect > haspect {
			scx = 1
			scy = haspect / oaspect
		} else {
			scx = vaspect * oaspect
			scy = 1
		}
		gl.Viewport(0, 0, gl.Sizei(ww), gl.Sizei(wh))
		gl.LoadIdentity()
		gl.Scaled(gl.Double(scx), gl.Double(scy), 1)
	})
	if !*unsync {
		glfw.SetSwapInterval(1)
	}
	glfw.SetWindowTitle(*in)
	for i := 0; i < ntex; i++ {
		texinit(i + 1)
	}
	factorloc := shinit()
	initquad()
	gl.Enable(gl.TEXTURE_2D)
	tbase := time.Now()
	pimg := img

	flushing := false
	for glfw.WindowParam(glfw.Opened) == 1 {
		if seek != webm.BadTC {
			reader.Seek(seek)
			seek = webm.BadTC
			flushing = true
		}

		gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
		t := time.Now()
		if flushing || (steps > 0 && (*notc || t.After(tbase.Add(img.Timecode)))) {
			pimg = img
			var ok bool
			var nimg webm.Frame
			nimg, ok = <-wchan
			if !ok {
				return
			}
			if nimg.Timecode == pimg.Timecode {
				log.Println("same timecode", img.Timecode)
			}
			if nimg.Rebase {
				tbase = time.Now().Add(-nimg.Timecode)
				flushing = false
			}
			if !flushing {
				steps--
				img = nimg
			}
		}
		lasttc = img.Timecode

		gl.ActiveTexture(gl.TEXTURE0)
		upload(1, img.Y, img.YStride, w, h)
		gl.ActiveTexture(gl.TEXTURE1)
		upload(2, img.Cb, img.CStride, w/2, h/2)
		gl.ActiveTexture(gl.TEXTURE2)
		upload(3, img.Cr, img.CStride, w/2, h/2)
		if *blend {
			gl.Uniform1f(factorloc, factor(t,
				tbase.Add(pimg.Timecode),
				tbase.Add(img.Timecode)))
			gl.ActiveTexture(gl.TEXTURE3)
			upload(4, pimg.Y, pimg.YStride, w, h)
			gl.ActiveTexture(gl.TEXTURE4)
			upload(5, pimg.Cb, pimg.CStride, w/2, h/2)
			gl.ActiveTexture(gl.TEXTURE5)
			upload(6, pimg.Cr, pimg.CStride, w/2, h/2)
		}
		gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
		runtime.GC()
		glfw.SwapBuffers()
	}
}

type AudioWriter struct {
	ch       <-chan webm.Samples
	channels int
	active   bool
	sofar    int
	curr     webm.Samples
}

func (aw *AudioWriter) ProcessAudio(in, out []float32) {
	for sent, lo := 0, len(out); sent < lo; {
		if aw.sofar == len(aw.curr.Data) {
			aw.curr, aw.active = <-aw.ch
			aw.sofar = 0
			//log.Println("timecode", aw.curr.Timecode)
		}
		s := copy(out[sent:], aw.curr.Data[aw.sofar:])
		sent += s
		aw.sofar += s
	}
}

func apresent(wchan <-chan webm.Samples, audio *webm.Audio) {
	chk := func(err error) {
		if err != nil {
			panic(err)
		}
	}
	channels := int(audio.Channels)
	aw := AudioWriter{ch: wchan, channels: channels, active: true}
	stream, err := portaudio.OpenDefaultStream(0, channels,
		audio.SamplingFrequency, 0, &aw)
	defer stream.Close()
	chk(err)
	chk(stream.Start())
	defer stream.Stop()
	for aw.active {
		time.Sleep(time.Second)
	}
}

func main() {
	flag.Parse()

	var err error
	var wm webm.WebM
	r, err := os.Open(*in)
	defer r.Close()
	if err != nil {
		log.Panic("Unable to open file " + *in)
	}
	reader, err := webm.Parse(r, &wm)
	if err != nil {
		log.Panic("Unable to parse file:", err)
	}
	duration = wm.GetDuration()

	splitter := webm.NewSplitter(reader.Chan)

	var vtrack *webm.TrackEntry
	var vstream *webm.Stream
	if !*justaudio {
		vtrack = wm.FindFirstVideoTrack()
		fduration = vtrack.GetDefaultDuration()
	}
	if vtrack != nil {
		vstream = webm.NewStream(vtrack)
	}
	var astream *webm.Stream
	var atrack *webm.TrackEntry
	if !*justvideo {
		atrack = wm.FindFirstAudioTrack()
	}
	if atrack != nil {
		astream = webm.NewStream(atrack)
	}
	splitter.Split(astream, vstream)
	switch {
	case astream != nil && vstream != nil:
		go apresent(astream.AudioChannel(), &atrack.Audio)
		fallthrough
	case vstream != nil:
		vpresent(vstream.VideoChannel(), reader)
	case astream != nil:
		apresent(astream.AudioChannel(), &atrack.Audio)
	}
}
<|endoftext|>


<commit_before>package geom

// PathElementType is an enumeration representing the different kinds of path
// elements supported by 2D paths.
type PathElementType int

const (
	MoveTo PathElementType = iota
	LineTo
	QuadCurveTo
	CubicCurveTo
	ClosePath
)

// A PathElement represents a single step in a 2D path, it's composed of a type
// that must be one of the constant values of PathElementType, and a list of 2D
// points that are interpreted differently based on the element's type.
type PathElement struct {
	Type   PathElementType
	Points [3]Point
}

// A Path is a drawable representation of an arbitrary shape, it's copmosed of a
// list of elements describing each step of the whole drawing operation.
type Path struct {
	Elements []PathElement
}

// The Shape interface represents values that can be converted to 2D paths.
type Shape interface {

	// Returns a 2D path representing the abstract shape that satisifed the
	// interface.
	//
	// The returned path shouldn't be retained by the shape or any other part
	// of the program, the caller of that method is considered the owner of the
	// returned value and is free to modify it.
	Path() Path
}

// MakePath returns a Path value initialized with a slice of elements with the
// capacity given as argument.
func MakePath(cap int) Path {
	return Path{
		Elements: make([]PathElement, 0, cap),
	}
}

// AppendPath concatenates the Path given as second arugment to the one given as
// first argument. It behaves in a similar way to the builtin `append` function,
// the Path value given as first argument may or may not be modified by the append
// operation.
func AppendPath(path Path, other Path) Path {
	return Path{Elements: append(path.Elements, other.Elements...)}
}

// AppendRect efficiently append a rectangle to a Path and returns the modified
// value.
//
// Calling this function is equivalent to calling:
//
//	path = AppendPath(path, rect.Path())
//
// but the implementation is more optimized and avoid unnecessary memory
// allocations.
func AppendRect(path Path, rect Rect) Path {
	x0 := rect.X
	y0 := rect.Y
	x1 := rect.X + rect.W
	y1 := rect.Y + rect.H
	return AppendPolygon(path, Point{x0, y0}, Point{x1, y0}, Point{x1, y1}, Point{x0, y1})
}

// AppendPolygon appends an arbitrary polygon to a path and returns the modified
// value, the polygon is made of the points given as arguments to the function.
func AppendPolygon(path Path, points ...Point) Path {
	if len(points) != 0 {
		path.MoveTo(points[0])

		for _, p := range points[1:] {
			path.LineTo(p)
		}

		path.Close()
	}

	return path
}

// The MoveTo method appends a path element that moves the current path position
// to the point given as argument.
func (path *Path) MoveTo(p Point) {
	path.append(PathElement{
		Type:   MoveTo,
		Points: [...]Point{p, {}, {}},
	})
}

// The LineTo method appends a path element that draws a line from the current
// path position to the point given as argument.
func (path *Path) LineTo(p Point) {
	path.ensureStartWithMoveTo()
	path.append(PathElement{
		Type:   LineTo,
		Points: [...]Point{p, {}, {}},
	})
}

// The QuadCurveTo method appends a path element that draws a quadriatic curve
// from the current path position to `p` and centered in `c`.
func (path *Path) QuadCurveTo(c Point, p Point) {
	path.ensureStartWithMoveTo()
	path.append(PathElement{
		Type:   QuadCurveTo,
		Points: [...]Point{c, p, {}},
	})
}

// The QuadCurveTo method appends a path element that draws a cubic curve from
// the current path position to `p` with centers in `c1` and `c2`.
func (path *Path) CubicCurveTo(c1 Point, c2 Point, p Point) {
	path.ensureStartWithMoveTo()
	path.append(PathElement{
		Type:   CubicCurveTo,
		Points: [...]Point{c1, c2, p},
	})
}

// The Close method appends a path element that closes the current shape by
// drawing a line between the current path position and the last move-to element
// added to the path.
func (path *Path) Close() {
	path.append(PathElement{
		Type: ClosePath,
	})
}

// The Clear method erases every element in the path.
func (path *Path) Clear() {
	path.Elements = path.Elements[:0]
}

// The Copy method creates a copy of the path and returns it, the returned value
// and the receiver do not share the same slice of elements and can be safely
// modified independently.
func (path *Path) Copy() Path {
	if path.Empty() {
		return Path{}
	}

	p := Path{
		Elements: make([]PathElement, len(path.Elements)),
	}

	copy(p.Elements, path.Elements)
	return p
}

// The LastPoint method returns the 2D coordinates of the current path position.
func (path *Path) LastPoint() Point {
	return path.lastPointAt(len(path.Elements) - 1)
}

func (path *Path) lastPointAt(n int) Point {
	if n < 0 {
		return Point{}
	}

	switch e := path.Elements[n-1]; e.Type {
	case MoveTo, LineTo:
		return e.Points[0]

	case QuadCurveTo:
		return e.Points[1]

	case CubicCurveTo:
		return e.Points[2]

	default:
		return path.lastPointAt(n - 1)
	}
}

// The Empty checks checks if the path is empty, which means it has no element.
func (path *Path) Empty() bool {
	return len(path.Elements) == 0
}

// The Path method satisfies the Shape interface by returning a copy of the path
// it is called on.
func (path Path) Path() Path {
	return path.Copy()
}

func (path *Path) append(e PathElement) {
	path.Elements = append(path.Elements, e)
}

func (path *Path) ensureStartWithMoveTo() {
	if n := len(path.Elements); n == 0 {
		// Empty paths must start with a MoveTo element.
		path.MoveTo(Point{})

	} else if path.Elements[n-1].Type == ClosePath {
		// When a subpath is closed the path that contains more elements must
		// be restarted with a MoveTo as well.
		path.MoveTo(path.LastPoint())
	}
}
<commit_msg>update path documentation<commit_after>package geom

// PathElementType is an enumeration representing the different kinds of path
// elements supported by 2D paths.
type PathElementType int

const (
	// MoveTo is used for path elements that move the path position to a new
	// location.
	MoveTo PathElementType = iota

	// LineTo is used for path elements that move the path position to a new
	// location while drawin a straight line.
	LineTo

	// QuandCurveTo is used for path elements that draw a quadriatic cruve.
	QuadCurveTo

	// CubicCurveTo is used for path elements that draw a cubic curve.
	CubicCurveTo

	// ClosePath is used for path elements that terminate drawing of a sub-path.
	ClosePath
)

// A PathElement represents a single step in a 2D path, it's composed of a type
// that must be one of the constant values of PathElementType, and a list of 2D
// points that are interpreted differently based on the element's type.
type PathElement struct {
	Type   PathElementType
	Points [3]Point
}

// A Path is a drawable representation of an arbitrary shape, it's copmosed of a
// list of elements describing each step of the whole drawing operation.
type Path struct {
	Elements []PathElement
}

// The Shape interface represents values that can be converted to 2D paths.
type Shape interface {

	// Returns a 2D path representing the abstract shape that satisifed the
	// interface.
	//
	// The returned path shouldn't be retained by the shape or any other part
	// of the program, the caller of that method is considered the owner of the
	// returned value and is free to modify it.
	Path() Path
}

// MakePath returns a Path value initialized with a slice of elements with the
// capacity given as argument.
func MakePath(cap int) Path {
	return Path{
		Elements: make([]PathElement, 0, cap),
	}
}

// AppendPath concatenates the Path given as second arugment to the one given as
// first argument. It behaves in a similar way to the builtin `append` function,
// the Path value given as first argument may or may not be modified by the append
// operation.
func AppendPath(path Path, other Path) Path {
	return Path{Elements: append(path.Elements, other.Elements...)}
}

// AppendRect efficiently append a rectangle to a Path and returns the modified
// value.
//
// Calling this function is equivalent to calling:
//
//	path = AppendPath(path, rect.Path())
//
// but the implementation is more optimized and avoid unnecessary memory
// allocations.
func AppendRect(path Path, rect Rect) Path {
	x0 := rect.X
	y0 := rect.Y
	x1 := rect.X + rect.W
	y1 := rect.Y + rect.H
	return AppendPolygon(path, Point{x0, y0}, Point{x1, y0}, Point{x1, y1}, Point{x0, y1})
}

// AppendPolygon appends an arbitrary polygon to a path and returns the modified
// value, the polygon is made of the points given as arguments to the function.
func AppendPolygon(path Path, points ...Point) Path {
	if len(points) != 0 {
		path.MoveTo(points[0])

		for _, p := range points[1:] {
			path.LineTo(p)
		}

		path.Close()
	}

	return path
}

// MoveTo appends a path element that moves the current path position to the
// point given as argument.
func (path *Path) MoveTo(p Point) {
	path.append(PathElement{
		Type:   MoveTo,
		Points: [...]Point{p, {}, {}},
	})
}

// LineTo appends a path element that draws a line from the current path
// position to the point given as argument.
func (path *Path) LineTo(p Point) {
	path.ensureStartWithMoveTo()
	path.append(PathElement{
		Type:   LineTo,
		Points: [...]Point{p, {}, {}},
	})
}

// QuadCurveTo appends a path element that draws a quadriatic curve from the
// current path position to `p` and centered in `c`.
func (path *Path) QuadCurveTo(c Point, p Point) {
	path.ensureStartWithMoveTo()
	path.append(PathElement{
		Type:   QuadCurveTo,
		Points: [...]Point{c, p, {}},
	})
}

// QuadCurveTo appends a path element that draws a cubic curve from the current
// path position to `p` with centers in `c1` and `c2`.
func (path *Path) CubicCurveTo(c1 Point, c2 Point, p Point) {
	path.ensureStartWithMoveTo()
	path.append(PathElement{
		Type:   CubicCurveTo,
		Points: [...]Point{c1, c2, p},
	})
}

// Close appends a path element that closes the current shape by drawing a line
// between the current path position and the last move-to element added to the
// path.
func (path *Path) Close() {
	path.append(PathElement{
		Type: ClosePath,
	})
}

// Clear erases every element in the path.
func (path *Path) Clear() {
	path.Elements = path.Elements[:0]
}

// Copy creates a copy of the path and returns it, the returned value and the
// receiver do not share the same slice of elements and can be safely modified
// independently.
func (path *Path) Copy() Path {
	if path.Empty() {
		return Path{}
	}

	p := Path{
		Elements: make([]PathElement, len(path.Elements)),
	}

	copy(p.Elements, path.Elements)
	return p
}

// LastPoint returns the 2D coordinates of the current path position.
func (path *Path) LastPoint() Point {
	return path.lastPointAt(len(path.Elements) - 1)
}

func (path *Path) lastPointAt(n int) Point {
	if n < 0 {
		return Point{}
	}

	switch e := path.Elements[n-1]; e.Type {
	case MoveTo, LineTo:
		return e.Points[0]

	case QuadCurveTo:
		return e.Points[1]

	case CubicCurveTo:
		return e.Points[2]

	default:
		return path.lastPointAt(n - 1)
	}
}

// Empty checks if the path is empty, which means it has no element.
func (path *Path) Empty() bool {
	return len(path.Elements) == 0
}

// Path satisfies the Shape interface by returning a copy of the path it is
// called on.
func (path Path) Path() Path {
	return path.Copy()
}

func (path *Path) append(e PathElement) {
	path.Elements = append(path.Elements, e)
}

func (path *Path) ensureStartWithMoveTo() {
	if n := len(path.Elements); n == 0 {
		// Empty paths must start with a MoveTo element.
		path.MoveTo(Point{})

	} else if path.Elements[n-1].Type == ClosePath {
		// When a subpath is closed the path that contains more elements must
		// be restarted with a MoveTo as well.
		path.MoveTo(path.LastPoint())
	}
}
<|endoftext|>