<commit_before>// Package ping tries to ping a HTTP server through different ways
// Connection, Session (Head), Get and Post
package ping

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"reflect"
	"regexp"
	"strings"
	"time"

	"github.com/mehrdadrad/mylg/cli"
)

// Ping represents HTTP ping request
type Ping struct {
	url     string
	host    string
	timeout time.Duration
	count   int
	method  string
	buf     io.Reader
	rAddr   net.Addr
	nsTime  time.Duration
	conn    net.Conn
}

// Result holds Ping result
type Result struct {
	StatusCode int
	ConnTime   float64
	TotalTime  float64
	Size       int
	Proto      string
	Server     string
	Status     string
}

// NewPing validate and constructs request object
func NewPing(URL string, timeout time.Duration) (*Ping, error) {
	URL, flag := cli.Flag(URL)
	// help
	if _, ok := flag["help"]; ok || URL == "" {
		help()
		return nil, fmt.Errorf("")
	}
	URL = Normalize(URL)
	u, err := url.Parse(URL)
	if err != nil {
		return &Ping{}, fmt.Errorf("cannot parse url")
	}
	sTime := time.Now()
	ipAddr, err := net.ResolveIPAddr("ip", u.Host)
	if err != nil {
		return &Ping{}, fmt.Errorf("cannot resolve %s: Unknown host", u.Host)
	}

	p := &Ping{
		url:     URL,
		host:    u.Host,
		rAddr:   ipAddr,
		nsTime:  time.Since(sTime),
		timeout: timeout,
	}

	// set count
	p.count = cli.SetFlag(flag, "c", 4).(int)
	// set method
	p.method = cli.SetFlag(flag, "m", "HEAD").(string)
	// set buff (post)
	buf := cli.SetFlag(flag, "d", "mylg").(string)
	p.buf = strings.NewReader(buf)
	return p, nil
}

// Normalize fixes scheme
func Normalize(URL string) string {
	re := regexp.MustCompile(`(?i)https{0,1}://`)
	if !re.MatchString(URL) {
		URL = fmt.Sprintf("http://%s", URL)
	}
	return URL
}

// ping tries to create a TCP connection
func (p *Ping) pingConn() (Result, bool) {
	var (
		r     Result
		err   error
		sTime time.Time
	)

	sTime = time.Now()
	p.conn, err = net.DialTimeout("tcp", p.url, p.timeout*time.Second)
	r.ConnTime = time.Since(sTime).Seconds()
	if err != nil {
		print(err.Error())
		return r, false
	}

	p.rAddr = p.conn.RemoteAddr()
	p.conn.Close()
	return r, true
}

// pingHeadLoop tries number of connection
// with header information
func (p *Ping) pingHeadLoop() {
	pStrPrefix := "HTTP Response seq=%d, "
	pStrSuffix := "proto=%s, status=%d, time=%.3f ms\n"
	fmt.Printf("HPING %s (%s), Method: HEAD, DNSLookup: %.4f ms\n", p.host, p.rAddr, p.nsTime.Seconds()*1000)
	for i := 0; i < p.count; i++ {
		if r, ok := p.pingHead(); ok {
			fmt.Printf(pStrPrefix+pStrSuffix, i, r.Proto, r.StatusCode, r.TotalTime*1000)
		} else {
			fmt.Printf(pStrPrefix+"timeout\n", i)
		}
	}
}

// pingGetLoop tries number of connection
// with header information
func (p *Ping) pingGetLoop() {
	pStrPrefix := "HTTP Response seq=%d, "
	pStrSuffix := "proto=%s, status=%d, size=%d Bytes, time=%.3f ms\n"
	fmt.Printf("HPING %s (%s), Method: GET, DNSLookup: %.4f ms\n", p.host, p.rAddr, p.nsTime.Seconds()*1000)
	for i := 0; i < p.count; i++ {
		if r, ok := p.pingGet(); ok {
			fmt.Printf(pStrPrefix+pStrSuffix, i, r.Proto, r.StatusCode, r.Size, r.TotalTime*1000)
		} else {
			fmt.Printf(pStrPrefix+"timeout\n", i)
		}
	}
}

// pingGetLoop tries number of connection
// with header information
func (p *Ping) pingPostLoop() {
	pStrPrefix := "HTTP Response seq=%d, "
	pStrSuffix := "proto=%s, status=%d, size=%d Bytes, time=%.3f ms\n"
	fmt.Printf("HPING %s (%s), Method: POST, DNSLookup: %.4f ms\n", p.host, 0, p.nsTime.Seconds()*1000)
	for i := 0; i < p.count; i++ {
		if r, ok := p.pingPost(); ok {
			fmt.Printf(pStrPrefix+pStrSuffix, i, r.Proto, r.StatusCode, r.Size, r.TotalTime*1000)
		} else {
			fmt.Printf(pStrPrefix+"timeout\n", i)
		}
	}
}

// pingGet tries to ping a web server through http
func (p *Ping) pingGet() (Result, bool) {
	var (
		r     Result
		sTime time.Time
	)

	client := &http.Client{Timeout: 2 * time.Second}
	sTime = time.Now()
	resp, err := client.Get(p.url)
	if err != nil {
		return r, false
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	r.Size = len(body)
	r.TotalTime = time.Since(sTime).Seconds()
	if err != nil {
		return r, false
	}
	r.StatusCode = resp.StatusCode
	r.Proto = resp.Proto
	return r, true
}

// pingHead tries to ping a web server through http
func (p *Ping) pingHead() (Result, bool) {
	var (
		r     Result
		sTime time.Time
	)

	client := &http.Client{Timeout: 2 * time.Second}
	sTime = time.Now()
	resp, err := client.Head(p.url)
	if err != nil {
		return r, false
	}
	r.TotalTime = time.Since(sTime).Seconds()
	if err != nil {
		return r, false
	}
	r.StatusCode = resp.StatusCode
	r.Proto = resp.Proto
	return r, true
}

// pingPost tries to ping a web server through http
func (p *Ping) pingPost() (Result, bool) {
	var (
		r     Result
		sTime time.Time
	)

	client := &http.Client{Timeout: 2 * time.Second}
	sTime = time.Now()
	resp, err := client.Post(p.url, "text/plain", p.buf)
	if err != nil {
		return r, false
	}
	r.TotalTime = time.Since(sTime).Seconds()
	if err != nil {
		return r, false
	}
	r.StatusCode = resp.StatusCode
	r.Proto = resp.Proto
	return r, true
}

// pingNetHead tries to execute head command
func (p *Ping) pingNetHead() (Result, bool) {
	var (
		r     Result
		b     = make([]byte, 512)
		err   error
		sTime time.Time
	)

	sTime = time.Now()
	p.conn, err = net.DialTimeout("tcp", p.host+":80", p.timeout*time.Second)

	if err != nil {
		print(err.Error())
		return r, false
	}

	fmt.Fprintf(p.conn, "HEAD / HTTP/1.1\r\n\r\n")
	reader := bufio.NewReader(p.conn)
	n, _ := reader.Read(b)
	for key, regex := range map[string]string{"Proto": `(HTTP/\d\.\d)`, "Status": `HTTP/\d\.\d\s+(\d+)`, "Server": `server:\s+(.*)\n`} {
		re := regexp.MustCompile(regex)
		a := re.FindSubmatch(b[:n])
		if len(a) == 2 {
			f := reflect.ValueOf(&r).Elem().FieldByName(key)
			f.SetString(string(a[1]))
		}
	}
	r.TotalTime = time.Since(sTime).Seconds()

	p.rAddr = p.conn.RemoteAddr()
	p.conn.Close()
	return r, true

}

// Run tries to run ping loop based on the method
func (p *Ping) Run() {
	switch p.method {
	case "HEAD":
		p.pingHeadLoop()
	case "GET":
		p.pingGetLoop()
	case "POST":
		p.pingPostLoop()
	}
}

// help shows ping help
func help() {
	println(`
    usage:
          hping [-c count][-m method][-d data] url

    options:
          -c count       Send 'count' requests (default: 4)
          -m method      HTTP methods: GET/POST/HEAD (default: HEAD)
          -d data        Sending the given data (text/json) (default: "mylg")
	`)
}
<commit_msg>fixed post len<commit_after>// Package ping tries to ping a HTTP server through different ways
// Connection, Session (Head), Get and Post
package ping

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"reflect"
	"regexp"
	"strings"
	"time"

	"github.com/mehrdadrad/mylg/cli"
)

// Ping represents HTTP ping request
type Ping struct {
	url     string
	host    string
	timeout time.Duration
	count   int
	method  string
	buf     string
	rAddr   net.Addr
	nsTime  time.Duration
	conn    net.Conn
}

// Result holds Ping result
type Result struct {
	StatusCode int
	ConnTime   float64
	TotalTime  float64
	Size       int
	Proto      string
	Server     string
	Status     string
}

// NewPing validate and constructs request object
func NewPing(URL string, timeout time.Duration) (*Ping, error) {
	URL, flag := cli.Flag(URL)
	// help
	if _, ok := flag["help"]; ok || URL == "" {
		help()
		return nil, fmt.Errorf("")
	}
	URL = Normalize(URL)
	u, err := url.Parse(URL)
	if err != nil {
		return &Ping{}, fmt.Errorf("cannot parse url")
	}
	sTime := time.Now()
	ipAddr, err := net.ResolveIPAddr("ip", u.Host)
	if err != nil {
		return &Ping{}, fmt.Errorf("cannot resolve %s: Unknown host", u.Host)
	}

	p := &Ping{
		url:     URL,
		host:    u.Host,
		rAddr:   ipAddr,
		nsTime:  time.Since(sTime),
		timeout: timeout,
	}

	// set count
	p.count = cli.SetFlag(flag, "c", 4).(int)
	// set method
	p.method = cli.SetFlag(flag, "m", "HEAD").(string)
	// set buff (post)
	buf := cli.SetFlag(flag, "d", "mylg").(string)
	p.buf = buf
	return p, nil
}

// Normalize fixes scheme
func Normalize(URL string) string {
	re := regexp.MustCompile(`(?i)https{0,1}://`)
	if !re.MatchString(URL) {
		URL = fmt.Sprintf("http://%s", URL)
	}
	return URL
}

// ping tries to create a TCP connection
func (p *Ping) pingConn() (Result, bool) {
	var (
		r     Result
		err   error
		sTime time.Time
	)

	sTime = time.Now()
	p.conn, err = net.DialTimeout("tcp", p.url, p.timeout*time.Second)
	r.ConnTime = time.Since(sTime).Seconds()
	if err != nil {
		print(err.Error())
		return r, false
	}

	p.rAddr = p.conn.RemoteAddr()
	p.conn.Close()
	return r, true
}

// pingHeadLoop tries number of connection
// with header information
func (p *Ping) pingHeadLoop() {
	pStrPrefix := "HTTP Response seq=%d, "
	pStrSuffix := "proto=%s, status=%d, time=%.3f ms\n"
	fmt.Printf("HPING %s (%s), Method: HEAD, DNSLookup: %.4f ms\n", p.host, p.rAddr, p.nsTime.Seconds()*1000)
	for i := 0; i < p.count; i++ {
		if r, ok := p.pingHead(); ok {
			fmt.Printf(pStrPrefix+pStrSuffix, i, r.Proto, r.StatusCode, r.TotalTime*1000)
		} else {
			fmt.Printf(pStrPrefix+"timeout\n", i)
		}
	}
}

// pingGetLoop tries number of connection
// with header information
func (p *Ping) pingGetLoop() {
	pStrPrefix := "HTTP Response seq=%d, "
	pStrSuffix := "proto=%s, status=%d, size=%d Bytes, time=%.3f ms\n"
	fmt.Printf("HPING %s (%s), Method: GET, DNSLookup: %.4f ms\n", p.host, p.rAddr, p.nsTime.Seconds()*1000)
	for i := 0; i < p.count; i++ {
		if r, ok := p.pingGet(); ok {
			fmt.Printf(pStrPrefix+pStrSuffix, i, r.Proto, r.StatusCode, r.Size, r.TotalTime*1000)
		} else {
			fmt.Printf(pStrPrefix+"timeout\n", i)
		}
	}
}

// pingGetLoop tries number of connection
// with header information
func (p *Ping) pingPostLoop() {
	pStrPrefix := "HTTP Response seq=%d, "
	pStrSuffix := "proto=%s, status=%d, size=%d Bytes, time=%.3f ms\n"
	fmt.Printf("HPING %s (%s), Method: POST, DNSLookup: %.4f ms\n", p.host, p.rAddr, p.nsTime.Seconds()*1000)
	for i := 0; i < p.count; i++ {
		if r, ok := p.pingPost(); ok {
			fmt.Printf(pStrPrefix+pStrSuffix, i, r.Proto, r.StatusCode, r.Size, r.TotalTime*1000)
		} else {
			fmt.Printf(pStrPrefix+"timeout\n", i)
		}
	}
}

// pingGet tries to ping a web server through http
func (p *Ping) pingGet() (Result, bool) {
	var (
		r     Result
		sTime time.Time
	)

	client := &http.Client{Timeout: 2 * time.Second}
	sTime = time.Now()
	resp, err := client.Get(p.url)
	if err != nil {
		return r, false
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	r.Size = len(body)
	r.TotalTime = time.Since(sTime).Seconds()
	if err != nil {
		return r, false
	}
	r.StatusCode = resp.StatusCode
	r.Proto = resp.Proto
	return r, true
}

// pingHead tries to ping a web server through http
func (p *Ping) pingHead() (Result, bool) {
	var (
		r     Result
		sTime time.Time
	)

	client := &http.Client{Timeout: 2 * time.Second}
	sTime = time.Now()
	resp, err := client.Head(p.url)
	if err != nil {
		return r, false
	}
	r.TotalTime = time.Since(sTime).Seconds()
	if err != nil {
		return r, false
	}
	r.StatusCode = resp.StatusCode
	r.Proto = resp.Proto
	return r, true
}

// pingPost tries to ping a web server through http
func (p *Ping) pingPost() (Result, bool) {
	var (
		r     Result
		sTime time.Time
	)

	client := &http.Client{Timeout: 2 * time.Second}
	sTime = time.Now()
	r.Size = len(p.buf)
	reader := strings.NewReader(p.buf)
	resp, err := client.Post(p.url, "text/plain", reader)
	if err != nil {
		return r, false
	}
	r.TotalTime = time.Since(sTime).Seconds()
	if err != nil {
		return r, false
	}
	r.StatusCode = resp.StatusCode
	r.Proto = resp.Proto
	return r, true
}

// pingNetHead tries to execute head command
func (p *Ping) pingNetHead() (Result, bool) {
	var (
		r     Result
		b     = make([]byte, 512)
		err   error
		sTime time.Time
	)

	sTime = time.Now()
	p.conn, err = net.DialTimeout("tcp", p.host+":80", p.timeout*time.Second)

	if err != nil {
		print(err.Error())
		return r, false
	}

	fmt.Fprintf(p.conn, "HEAD / HTTP/1.1\r\n\r\n")
	reader := bufio.NewReader(p.conn)
	n, _ := reader.Read(b)
	for key, regex := range map[string]string{"Proto": `(HTTP/\d\.\d)`, "Status": `HTTP/\d\.\d\s+(\d+)`, "Server": `server:\s+(.*)\n`} {
		re := regexp.MustCompile(regex)
		a := re.FindSubmatch(b[:n])
		if len(a) == 2 {
			f := reflect.ValueOf(&r).Elem().FieldByName(key)
			f.SetString(string(a[1]))
		}
	}
	r.TotalTime = time.Since(sTime).Seconds()

	p.rAddr = p.conn.RemoteAddr()
	p.conn.Close()
	return r, true

}

// Run tries to run ping loop based on the method
func (p *Ping) Run() {
	switch p.method {
	case "HEAD":
		p.pingHeadLoop()
	case "GET":
		p.pingGetLoop()
	case "POST":
		p.pingPostLoop()
	}
}

// help shows ping help
func help() {
	println(`
    usage:
          hping [-c count][-m method][-d data] url

    options:
          -c count       Send 'count' requests (default: 4)
          -m method      HTTP methods: GET/POST/HEAD (default: HEAD)
          -d data        Sending the given data (text/json) (default: "mylg")
	`)
}
<|endoftext|>


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

// Package inpututil provides utility functions of input like keyboard or mouse.
//
// Note: This package is experimental and API might be changed.
package inpututil

import (
	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/internal/hooks"
	"github.com/hajimehoshi/ebiten/internal/sync"
)

type inputState struct {
	keyStates     map[ebiten.Key]int
	prevKeyStates map[ebiten.Key]int

	mouseButtonStates     map[ebiten.MouseButton]int
	prevMouseButtonStates map[ebiten.MouseButton]int

	gamepadButtonStates     map[int]map[ebiten.GamepadButton]int
	prevGamepadButtonStates map[int]map[ebiten.GamepadButton]int

	touchStates map[int]int

	m sync.RWMutex
}

var theInputState = &inputState{
	keyStates:     map[ebiten.Key]int{},
	prevKeyStates: map[ebiten.Key]int{},

	mouseButtonStates:     map[ebiten.MouseButton]int{},
	prevMouseButtonStates: map[ebiten.MouseButton]int{},

	gamepadButtonStates:     map[int]map[ebiten.GamepadButton]int{},
	prevGamepadButtonStates: map[int]map[ebiten.GamepadButton]int{},

	touchStates: map[int]int{},
}

func init() {
	hooks.AppendHookOnUpdate(func() error {
		theInputState.update()
		return nil
	})
}

func (i *inputState) update() {
	i.m.Lock()
	defer i.m.Unlock()

	// Keyboard
	for k := ebiten.Key(0); k <= ebiten.KeyMax; k++ {
		i.prevKeyStates[k] = i.keyStates[k]
		if ebiten.IsKeyPressed(k) {
			i.keyStates[k]++
		} else {
			i.keyStates[k] = 0
		}
	}

	// Mouse
	for _, b := range []ebiten.MouseButton{
		ebiten.MouseButtonLeft,
		ebiten.MouseButtonRight,
		ebiten.MouseButtonMiddle,
	} {
		i.prevMouseButtonStates[b] = i.mouseButtonStates[b]
		if ebiten.IsMouseButtonPressed(b) {
			i.mouseButtonStates[b]++
		} else {
			i.mouseButtonStates[b] = 0
		}
	}

	// Gamepads

	// Reset the previous states first since some gamepad IDs might be already gone.
	for id := range i.prevGamepadButtonStates {
		for b := range i.prevGamepadButtonStates[id] {
			i.prevGamepadButtonStates[id][b] = 0
		}
	}
	ids := map[int]struct{}{}
	for _, id := range ebiten.GamepadIDs() {
		ids[id] = struct{}{}

		if _, ok := i.prevGamepadButtonStates[id]; !ok {
			i.prevGamepadButtonStates[id] = map[ebiten.GamepadButton]int{}
		}
		if _, ok := i.gamepadButtonStates[id]; !ok {
			i.gamepadButtonStates[id] = map[ebiten.GamepadButton]int{}
		}

		n := ebiten.GamepadButtonNum(id)
		for b := ebiten.GamepadButton(0); b < ebiten.GamepadButton(n); b++ {
			i.prevGamepadButtonStates[id][b] = i.gamepadButtonStates[id][b]
			if ebiten.IsGamepadButtonPressed(id, b) {
				i.gamepadButtonStates[id][b]++
			} else {
				i.gamepadButtonStates[id][b] = 0
			}
		}
	}
	idsToDelete := []int{}
	for id := range i.gamepadButtonStates {
		if _, ok := ids[id]; !ok {
			idsToDelete = append(idsToDelete, id)
		}
	}
	for _, id := range idsToDelete {
		delete(i.gamepadButtonStates, id)
	}

	// Touches
	ids = map[int]struct{}{}
	for _, t := range ebiten.Touches() {
		ids[t.ID()] = struct{}{}
		i.touchStates[t.ID()]++
	}
	idsToDelete = []int{}
	for id := range i.touchStates {
		if _, ok := ids[id]; !ok {
			idsToDelete = append(idsToDelete, id)
		}
	}
	for _, id := range idsToDelete {
		delete(i.touchStates, id)
	}
}

// IsKeyJustPressed returns a boolean value indicating
// whether the given key is pressed just in the current frame.
//
// IsKeyJustPressed is concurrent safe.
func IsKeyJustPressed(key ebiten.Key) bool {
	return KeyPressDuration(key) == 1
}

// IsKeyJustReleased returns a boolean value indicating
// whether the given key is released just in the current frame.
//
// IsKeyJustReleased is concurrent safe.
func IsKeyJustReleased(key ebiten.Key) bool {
	theInputState.m.RLock()
	r := theInputState.keyStates[key] == 0 && theInputState.prevKeyStates[key] > 0
	theInputState.m.RUnlock()
	return r
}

// KeyPressDuration returns how long the key is pressed in frames.
//
// KeyPressDuration is concurrent safe.
func KeyPressDuration(key ebiten.Key) int {
	theInputState.m.RLock()
	s := theInputState.keyStates[key]
	theInputState.m.RUnlock()
	return s
}

// IsMouseButtonJustPressed returns a boolean value indicating
// whether the given mouse button is pressed just in the current frame.
//
// IsMouseButtonJustPressed is concurrent safe.
func IsMouseButtonJustPressed(button ebiten.MouseButton) bool {
	return MouseButtonPressDuration(button) == 1
}

// IsMouseButtonJustReleased returns a boolean value indicating
// whether the given mouse button is released just in the current frame.
//
// IsMouseButtonJustReleased is concurrent safe.
func IsMouseButtonJustReleased(button ebiten.MouseButton) bool {
	theInputState.m.RLock()
	r := theInputState.mouseButtonStates[button] == 0 &&
		theInputState.prevMouseButtonStates[button] > 0
	theInputState.m.RUnlock()
	return r
}

// MouseButtonPressDuration returns how long the mouse button is pressed in frames.
//
// MouseButtonPressDuration is concurrent safe.
func MouseButtonPressDuration(button ebiten.MouseButton) int {
	theInputState.m.RLock()
	s := theInputState.mouseButtonStates[button]
	theInputState.m.RUnlock()
	return s
}

// IsGamepadButtonJustPressed returns a boolean value indicating
// whether the given gamepad button of the gamepad id is pressed just in the current frame.
//
// IsGamepadButtonJustPressed is concurrent safe.
func IsGamepadButtonJustPressed(id int, button ebiten.GamepadButton) bool {
	return GamepadButtonPressDuration(id, button) == 1
}

// IsGamepadButtonJustReleased returns a boolean value indicating
// whether the given gamepad button of the gamepad id is released just in the current frame.
//
// IsGamepadButtonJustReleased is concurrent safe.
func IsGamepadButtonJustReleased(id int, button ebiten.GamepadButton) bool {
	theInputState.m.RLock()
	prev := 0
	if _, ok := theInputState.prevGamepadButtonStates[id]; ok {
		prev = theInputState.prevGamepadButtonStates[id][button]
	}
	current := 0
	if _, ok := theInputState.gamepadButtonStates[id]; ok {
		current = theInputState.gamepadButtonStates[id][button]
	}
	theInputState.m.RUnlock()
	return current == 0 && prev > 0
}

// GamepadButtonPressDuration returns how long the gamepad button of the gamepad id is pressed in frames.
//
// GamepadButtonPressDuration is concurrent safe.
func GamepadButtonPressDuration(id int, button ebiten.GamepadButton) int {
	theInputState.m.RLock()
	s := 0
	if _, ok := theInputState.gamepadButtonStates[id]; ok {
		s = theInputState.gamepadButtonStates[id][button]
	}
	theInputState.m.RUnlock()
	return s
}

// IsJustTouched returns a boolean value indicating
// whether the given touch is pressed just in the current frame.
//
// IsJustTouched is concurrent safe.
func IsJustTouched(id int) bool {
	return TouchDuration(id) == 1
}

// TouchDuration returns how long the touch remains in frames.
//
// TouchDuration is concurrent safe.
func TouchDuration(id int) int {
	theInputState.m.RLock()
	s := theInputState.touchStates[id]
	theInputState.m.RUnlock()
	return s
}
<commit_msg>inpututil: Add IsTouchJustReleased (#504)<commit_after>// Copyright 2018 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package inpututil provides utility functions of input like keyboard or mouse.
//
// Note: This package is experimental and API might be changed.
package inpututil

import (
	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/internal/hooks"
	"github.com/hajimehoshi/ebiten/internal/sync"
)

type inputState struct {
	keyStates     map[ebiten.Key]int
	prevKeyStates map[ebiten.Key]int

	mouseButtonStates     map[ebiten.MouseButton]int
	prevMouseButtonStates map[ebiten.MouseButton]int

	gamepadButtonStates     map[int]map[ebiten.GamepadButton]int
	prevGamepadButtonStates map[int]map[ebiten.GamepadButton]int

	touchStates     map[int]int
	prevTouchStates map[int]int

	m sync.RWMutex
}

var theInputState = &inputState{
	keyStates:     map[ebiten.Key]int{},
	prevKeyStates: map[ebiten.Key]int{},

	mouseButtonStates:     map[ebiten.MouseButton]int{},
	prevMouseButtonStates: map[ebiten.MouseButton]int{},

	gamepadButtonStates:     map[int]map[ebiten.GamepadButton]int{},
	prevGamepadButtonStates: map[int]map[ebiten.GamepadButton]int{},

	touchStates: map[int]int{},
}

func init() {
	hooks.AppendHookOnUpdate(func() error {
		theInputState.update()
		return nil
	})
}

func (i *inputState) update() {
	i.m.Lock()
	defer i.m.Unlock()

	// Keyboard
	for k := ebiten.Key(0); k <= ebiten.KeyMax; k++ {
		i.prevKeyStates[k] = i.keyStates[k]
		if ebiten.IsKeyPressed(k) {
			i.keyStates[k]++
		} else {
			i.keyStates[k] = 0
		}
	}

	// Mouse
	for _, b := range []ebiten.MouseButton{
		ebiten.MouseButtonLeft,
		ebiten.MouseButtonRight,
		ebiten.MouseButtonMiddle,
	} {
		i.prevMouseButtonStates[b] = i.mouseButtonStates[b]
		if ebiten.IsMouseButtonPressed(b) {
			i.mouseButtonStates[b]++
		} else {
			i.mouseButtonStates[b] = 0
		}
	}

	// Gamepads

	// Reset the previous states first since some gamepad IDs might be already gone.
	for id := range i.prevGamepadButtonStates {
		for b := range i.prevGamepadButtonStates[id] {
			i.prevGamepadButtonStates[id][b] = 0
		}
	}
	ids := map[int]struct{}{}
	for _, id := range ebiten.GamepadIDs() {
		ids[id] = struct{}{}

		if _, ok := i.prevGamepadButtonStates[id]; !ok {
			i.prevGamepadButtonStates[id] = map[ebiten.GamepadButton]int{}
		}
		if _, ok := i.gamepadButtonStates[id]; !ok {
			i.gamepadButtonStates[id] = map[ebiten.GamepadButton]int{}
		}

		n := ebiten.GamepadButtonNum(id)
		for b := ebiten.GamepadButton(0); b < ebiten.GamepadButton(n); b++ {
			i.prevGamepadButtonStates[id][b] = i.gamepadButtonStates[id][b]
			if ebiten.IsGamepadButtonPressed(id, b) {
				i.gamepadButtonStates[id][b]++
			} else {
				i.gamepadButtonStates[id][b] = 0
			}
		}
	}
	idsToDelete := []int{}
	for id := range i.gamepadButtonStates {
		if _, ok := ids[id]; !ok {
			idsToDelete = append(idsToDelete, id)
		}
	}
	for _, id := range idsToDelete {
		delete(i.gamepadButtonStates, id)
	}

	// Touches
	ids = map[int]struct{}{}

	// Reset the previous states first since some gamepad IDs might be already gone.
	for id := range i.prevTouchStates {
		i.prevTouchStates[id] = 0
	}

	for _, t := range ebiten.Touches() {
		ids[t.ID()] = struct{}{}
		i.prevTouchStates[t.ID()] = i.touchStates[t.ID()]
		i.touchStates[t.ID()]++
	}
	idsToDelete = []int{}
	for id := range i.touchStates {
		if _, ok := ids[id]; !ok {
			idsToDelete = append(idsToDelete, id)
		}
	}
	for _, id := range idsToDelete {
		delete(i.touchStates, id)
	}
}

// IsKeyJustPressed returns a boolean value indicating
// whether the given key is pressed just in the current frame.
//
// IsKeyJustPressed is concurrent safe.
func IsKeyJustPressed(key ebiten.Key) bool {
	return KeyPressDuration(key) == 1
}

// IsKeyJustReleased returns a boolean value indicating
// whether the given key is released just in the current frame.
//
// IsKeyJustReleased is concurrent safe.
func IsKeyJustReleased(key ebiten.Key) bool {
	theInputState.m.RLock()
	r := theInputState.keyStates[key] == 0 && theInputState.prevKeyStates[key] > 0
	theInputState.m.RUnlock()
	return r
}

// KeyPressDuration returns how long the key is pressed in frames.
//
// KeyPressDuration is concurrent safe.
func KeyPressDuration(key ebiten.Key) int {
	theInputState.m.RLock()
	s := theInputState.keyStates[key]
	theInputState.m.RUnlock()
	return s
}

// IsMouseButtonJustPressed returns a boolean value indicating
// whether the given mouse button is pressed just in the current frame.
//
// IsMouseButtonJustPressed is concurrent safe.
func IsMouseButtonJustPressed(button ebiten.MouseButton) bool {
	return MouseButtonPressDuration(button) == 1
}

// IsMouseButtonJustReleased returns a boolean value indicating
// whether the given mouse button is released just in the current frame.
//
// IsMouseButtonJustReleased is concurrent safe.
func IsMouseButtonJustReleased(button ebiten.MouseButton) bool {
	theInputState.m.RLock()
	r := theInputState.mouseButtonStates[button] == 0 &&
		theInputState.prevMouseButtonStates[button] > 0
	theInputState.m.RUnlock()
	return r
}

// MouseButtonPressDuration returns how long the mouse button is pressed in frames.
//
// MouseButtonPressDuration is concurrent safe.
func MouseButtonPressDuration(button ebiten.MouseButton) int {
	theInputState.m.RLock()
	s := theInputState.mouseButtonStates[button]
	theInputState.m.RUnlock()
	return s
}

// IsGamepadButtonJustPressed returns a boolean value indicating
// whether the given gamepad button of the gamepad id is pressed just in the current frame.
//
// IsGamepadButtonJustPressed is concurrent safe.
func IsGamepadButtonJustPressed(id int, button ebiten.GamepadButton) bool {
	return GamepadButtonPressDuration(id, button) == 1
}

// IsGamepadButtonJustReleased returns a boolean value indicating
// whether the given gamepad button of the gamepad id is released just in the current frame.
//
// IsGamepadButtonJustReleased is concurrent safe.
func IsGamepadButtonJustReleased(id int, button ebiten.GamepadButton) bool {
	theInputState.m.RLock()
	prev := 0
	if _, ok := theInputState.prevGamepadButtonStates[id]; ok {
		prev = theInputState.prevGamepadButtonStates[id][button]
	}
	current := 0
	if _, ok := theInputState.gamepadButtonStates[id]; ok {
		current = theInputState.gamepadButtonStates[id][button]
	}
	theInputState.m.RUnlock()
	return current == 0 && prev > 0
}

// GamepadButtonPressDuration returns how long the gamepad button of the gamepad id is pressed in frames.
//
// GamepadButtonPressDuration is concurrent safe.
func GamepadButtonPressDuration(id int, button ebiten.GamepadButton) int {
	theInputState.m.RLock()
	s := 0
	if _, ok := theInputState.gamepadButtonStates[id]; ok {
		s = theInputState.gamepadButtonStates[id][button]
	}
	theInputState.m.RUnlock()
	return s
}

// IsJustTouched returns a boolean value indicating
// whether the given touch is pressed just in the current frame.
//
// IsJustTouched is concurrent safe.
func IsJustTouched(id int) bool {
	return TouchDuration(id) == 1
}

// IsTouchJustReleased returns a boolean value indicating
// whether the given touch is released just in the current frame.
//
// IsTouchJustReleased is concurrent safe.
func IsTouchJustReleased(id int) bool {
	theInputState.m.RLock()
	r := theInputState.touchStates[id] == 0 && theInputState.prevTouchStates[id] > 0
	theInputState.m.RUnlock()
	return r
}

// TouchDuration returns how long the touch remains in frames.
//
// TouchDuration is concurrent safe.
func TouchDuration(id int) int {
	theInputState.m.RLock()
	s := theInputState.touchStates[id]
	theInputState.m.RUnlock()
	return s
}
<|endoftext|>


<commit_before>package raftgorums

import (
	"time"

	"golang.org/x/net/context"

	"github.com/Sirupsen/logrus"
	pb "github.com/relab/raft/raftgorums/raftpb"
)

func (r *Raft) handleOutgoing() error {
	// January 1, 1970 UTC.
	var lastCuReq time.Time

	for {
		select {
		case err := <-r.mem.get().SubError():
			// TODO If a node becomes unavailable and there is a
			// backup available in the same or an alternate region,
			// instantiate reconfiguratior. TODO How many errors
			// before a node is considered unavailable? If there is
			// no backup node available, don't do anything, but
			// schedule the reconfiguratior.
			r.logger.WithField("nodeid", err.NodeID).Warnln("Node unavailable")
		case req := <-r.cureqout:
			// TODO Use config.
			if time.Since(lastCuReq) < 100*time.Millisecond {
				continue
			}
			lastCuReq = time.Now()

			r.logger.WithField("matchindex", req.matchIndex).Warnln("Sending catch-up")
			ctx, cancel := context.WithTimeout(context.Background(), TCPHeartbeat*time.Millisecond)
			leader := r.mem.getNode(req.leaderID)
			_, err := leader.RaftClient.CatchMeUp(ctx, &pb.CatchMeUpRequest{
				FollowerID: r.id,
				NextIndex:  req.matchIndex + 1,
			})
			cancel()

			if err != nil {
				r.logger.WithError(err).Warnln("CatchMeUp failed")
			}
		case req := <-r.rvreqout:
			conf := r.mem.get()

			r.logger.WithField("conf", conf.NodeIDs()).Println("Sending request for vote")

			ctx, cancel := context.WithTimeout(context.Background(), TCPHeartbeat*time.Millisecond)
			res, err := conf.RequestVote(ctx, req)
			cancel()

			if err != nil {
				r.logger.WithError(err).Warnln("RequestVote failed")
			}

			if res == nil {
				continue
			}

			r.HandleRequestVoteResponse(res)

		case req := <-r.aereqout:
			next := make(map[uint32]uint64)
			nextIndex := req.PrevLogIndex + 1

			for nodeID, ch := range r.match {
				select {
				case index := <-ch:
					// TODO Acessing maxAppendEntries, safe but needs fix.
					atLeastMaxEntries := req.PrevLogIndex+1 > r.maxAppendEntries
					lessThenMaxEntriesBehind := index < req.PrevLogIndex+1-r.maxAppendEntries

					if atLeastMaxEntries && lessThenMaxEntriesBehind {
						r.logger.WithFields(logrus.Fields{
							"gorumsid": nodeID,
							"raftid":   r.id,
						}).Warnln("Server too far behind")
						index = req.PrevLogIndex + 1
					}
					next[nodeID] = index
					if index < nextIndex {
						nextIndex = index
					}
				default:
				}
			}

			// TODO This should be safe as it only accesses storage
			// which uses transactions. TODO It accesses
			// maxAppendEntries but this on does not change after
			// startup.
			entries := r.getNextEntries(nextIndex)
			e := uint64(len(entries))
			maxIndex := nextIndex + e - 1

			conf := r.mem.get()
			r.logger.WithField("conf", conf.NodeIDs()).Println("Sending append entries request")

			ctx, cancel := context.WithTimeout(context.Background(), TCPHeartbeat*time.Millisecond)
			res, err := conf.AppendEntries(ctx, req,
				// These functions will be executed concurrently.
				func(req pb.AppendEntriesRequest, nodeID uint32) *pb.AppendEntriesRequest {
					if index, ok := next[nodeID]; ok {
						req.PrevLogIndex = index - 1
						// TODO This should be safe as
						// it only accesses storage
						// which uses transactions.
						req.PrevLogTerm = r.logTerm(index - 1)
					}

					need := maxIndex - req.PrevLogIndex
					req.Entries = entries[e-need:]

					r.logger.WithFields(logrus.Fields{
						"prevlogindex": req.PrevLogIndex,
						"prevlogterm":  req.PrevLogTerm,
						"commitindex":  req.CommitIndex,
						"currentterm":  req.Term,
						"lenentries":   len(req.Entries),
						"gorumsid":     nodeID,
						"raftid":       r.id,
					}).Infoln("Sending AppendEntries")

					return &req
				},
			)

			if err != nil {
				r.logger.WithError(err).Warnln("AppendEntries failed")
			}

			if res == nil {
				continue
			}

			// Cancel on abort.
			if !res.Success {
				cancel()
			}

			r.HandleAppendEntriesResponse(res, res.Replies)
		}
	}
}
<commit_msg>raftgorums/outgoing.go: Remove useless logger field<commit_after>package raftgorums

import (
	"time"

	"golang.org/x/net/context"

	"github.com/Sirupsen/logrus"
	pb "github.com/relab/raft/raftgorums/raftpb"
)

func (r *Raft) handleOutgoing() error {
	// January 1, 1970 UTC.
	var lastCuReq time.Time

	for {
		select {
		case err := <-r.mem.get().SubError():
			// TODO If a node becomes unavailable and there is a
			// backup available in the same or an alternate region,
			// instantiate reconfiguratior. TODO How many errors
			// before a node is considered unavailable? If there is
			// no backup node available, don't do anything, but
			// schedule the reconfiguratior.
			r.logger.WithField("nodeid", err.NodeID).Warnln("Node unavailable")
		case req := <-r.cureqout:
			// TODO Use config.
			if time.Since(lastCuReq) < 100*time.Millisecond {
				continue
			}
			lastCuReq = time.Now()

			r.logger.WithField("matchindex", req.matchIndex).Warnln("Sending catch-up")
			ctx, cancel := context.WithTimeout(context.Background(), TCPHeartbeat*time.Millisecond)
			leader := r.mem.getNode(req.leaderID)
			_, err := leader.RaftClient.CatchMeUp(ctx, &pb.CatchMeUpRequest{
				FollowerID: r.id,
				NextIndex:  req.matchIndex + 1,
			})
			cancel()

			if err != nil {
				r.logger.WithError(err).Warnln("CatchMeUp failed")
			}
		case req := <-r.rvreqout:
			conf := r.mem.get()

			r.logger.WithField("conf", conf.NodeIDs()).Println("Sending request for vote")

			ctx, cancel := context.WithTimeout(context.Background(), TCPHeartbeat*time.Millisecond)
			res, err := conf.RequestVote(ctx, req)
			cancel()

			if err != nil {
				r.logger.WithError(err).Warnln("RequestVote failed")
			}

			if res == nil {
				continue
			}

			r.HandleRequestVoteResponse(res)

		case req := <-r.aereqout:
			next := make(map[uint32]uint64)
			nextIndex := req.PrevLogIndex + 1

			for nodeID, ch := range r.match {
				select {
				case index := <-ch:
					// TODO Acessing maxAppendEntries, safe but needs fix.
					atLeastMaxEntries := req.PrevLogIndex+1 > r.maxAppendEntries
					lessThenMaxEntriesBehind := index < req.PrevLogIndex+1-r.maxAppendEntries

					if atLeastMaxEntries && lessThenMaxEntriesBehind {
						r.logger.WithFields(logrus.Fields{
							"gorumsid": nodeID,
						}).Warnln("Server too far behind")
						index = req.PrevLogIndex + 1
					}
					next[nodeID] = index
					if index < nextIndex {
						nextIndex = index
					}
				default:
				}
			}

			// TODO This should be safe as it only accesses storage
			// which uses transactions. TODO It accesses
			// maxAppendEntries but this on does not change after
			// startup.
			entries := r.getNextEntries(nextIndex)
			e := uint64(len(entries))
			maxIndex := nextIndex + e - 1

			conf := r.mem.get()
			r.logger.WithField("conf", conf.NodeIDs()).Println("Sending append entries request")

			ctx, cancel := context.WithTimeout(context.Background(), TCPHeartbeat*time.Millisecond)
			res, err := conf.AppendEntries(ctx, req,
				// These functions will be executed concurrently.
				func(req pb.AppendEntriesRequest, nodeID uint32) *pb.AppendEntriesRequest {
					if index, ok := next[nodeID]; ok {
						req.PrevLogIndex = index - 1
						// TODO This should be safe as
						// it only accesses storage
						// which uses transactions.
						req.PrevLogTerm = r.logTerm(index - 1)
					}

					need := maxIndex - req.PrevLogIndex
					req.Entries = entries[e-need:]

					r.logger.WithFields(logrus.Fields{
						"prevlogindex": req.PrevLogIndex,
						"prevlogterm":  req.PrevLogTerm,
						"commitindex":  req.CommitIndex,
						"currentterm":  req.Term,
						"lenentries":   len(req.Entries),
						"gorumsid":     nodeID,
					}).Infoln("Sending AppendEntries")

					return &req
				},
			)

			if err != nil {
				r.logger.WithError(err).Warnln("AppendEntries failed")
			}

			if res == nil {
				continue
			}

			// Cancel on abort.
			if !res.Success {
				cancel()
			}

			r.HandleAppendEntriesResponse(res, res.Replies)
		}
	}
}
<|endoftext|>


<commit_before>package multi

import (
	"os"
	"path"
	"sync"

	"github.com/gotgo/fw/logging"
)

type ImageProcessor struct {

	// Uploader - required
	Uploader Uploader

	// LocalPath - defaults to os.TempDir()/downloads
	LocalPath string

	Log logging.Logger

	MaxHeight int
	MaxWidth  int

	downloader *TaskRun
	phasher    *TaskRun
	resizer    *TaskRun
	uploader   *TaskRun

	outstanding sync.WaitGroup
	complete    chan *ImageProcessorOutput
}

func (ip *ImageProcessor) setup() {
	uploader := ip.Uploader
	if uploader == nil {
		panic("uploader not set")
	}

	if ip.Log == nil {
		ip.Log = &logging.NoOpLogger{}
	}

	var tempFolder = ip.LocalPath
	if tempFolder == "" {
		tempFolder = path.Join(os.TempDir(), "downloads")
	}

	os.MkdirAll(tempFolder, 0777)

	ip.complete = make(chan *ImageProcessorOutput, 100)

	ip.downloader = &TaskRun{
		Action:       &FileDownloadTask{Folder: tempFolder},
		Concurrency:  10,
		MaxQueuedIn:  10 * 5,
		MaxQueuedOut: 10 * 10,
	}

	ip.phasher = &TaskRun{
		Action: &PHashTask{
			Log: ip.Log,
		},
		Concurrency:  2,
		MaxQueuedIn:  2,
		MaxQueuedOut: 100,
	}

	ip.resizer = &TaskRun{
		Action:       &ResizeImageTask{MaxHeight: ip.MaxHeight, MaxWidth: ip.MaxWidth},
		Concurrency:  6,
		MaxQueuedIn:  12,
		MaxQueuedOut: 100,
	}

	ip.uploader = &TaskRun{
		Action:       &FileUploadTask{Uploader: uploader},
		Concurrency:  8,
		MaxQueuedIn:  8 * 10,
		MaxQueuedOut: 100,
	}
}

func (p *ImageProcessor) Startup() {
	p.setup()
	p.doScavenge()
}

func (p *ImageProcessor) Shutdown() {
	p.downloader.Shutdown()
	p.outstanding.Wait()
}

func (p *ImageProcessor) Injest(url, filename string, ctx *DataContext) {
	in := &FileDownloadInput{
		Url:      url,
		Filename: filename,
	}

	p.downloader.Add(in, ctx)
}

func (p *ImageProcessor) doScavenge() {
	p.downloader.Startup()
	p.phasher.Startup()
	p.resizer.Startup()
	p.uploader.Startup()

	p.outstanding.Add(1)
	// phash
	go p.phash()
	p.outstanding.Add(1)
	// resize
	go p.resize()
	p.outstanding.Add(1)
	// upload
	go p.upload()
	p.outstanding.Add(1)
	// collector
	go p.wrapUp()
}

func (p *ImageProcessor) handleError(message string, result *TaskRunOutput) {
	p.Log.Error(message, result.Error())
	p.complete <- &ImageProcessorOutput{
		Error:   result.Error(),
		Context: result.Context,
	}
}

func (p *ImageProcessor) phash() {
	for dl := range p.downloader.Completed() {
		if dl.Error() != nil {
			p.handleError("download failed", dl)
		} else {
			result := dl.Context.Get(p.downloader.Name()).(*TaskRunResult)
			dlo := result.Output.(*FileDownloadOutput)
			p.phasher.Add(dlo.Path, dl.Context)
		}
	}
	p.phasher.Shutdown()
	p.outstanding.Done()
}

func (p *ImageProcessor) resize() {
	for ph := range p.phasher.Completed() {
		if ph.Error() != nil {
			p.handleError("Phash failed", ph)
		} else {
			result := ph.Context.Get(p.downloader.Name()).(*TaskRunResult)
			dlo := result.Output.(*FileDownloadOutput)
			p.resizer.Add(dlo.Path, ph.Context)
		}
	}
	p.resizer.Shutdown()
	p.outstanding.Done()
}

func (p *ImageProcessor) upload() {
	for rz := range p.resizer.Completed() {
		if rz.Error() != nil {
			p.handleError("resize failed", rz)
		} else {
			rzOut := rz.Output().(*ImageResizeOutput)
			p.uploader.Add(rzOut.FilePath, rz.Context)
		}
	}
	p.uploader.Shutdown()
	p.outstanding.Done()
}

func (p *ImageProcessor) wrapUp() {
	for result := range p.uploader.Completed() {
		if result.Error() != nil {
			p.handleError("upload failed", result)
		} else {
			dl := result.Previous(p.downloader.Name()).Output.(*FileDownloadOutput)
			dlin := result.Previous(p.downloader.Name()).Input.(*FileDownloadInput)
			rz := result.Previous(p.resizer.Name()).Output.(*ImageResizeOutput)
			phash := result.Previous(p.phasher.Name()).Output.(uint64)
			ul := result.Previous(p.uploader.Name()).Output.(*FileUploadOutput)

			if dl == nil || dlin == nil {
				panic("nil - download in or out")
			}
			if rz == nil {
				panic("nil - resizer")
			}
			if ul == nil {
				panic("nil - upload")
			}
			if result == nil {
				panic("nil - result")
			}

			r := &ImageProcessorOutput{
				DownloadSize:        dl.Size,
				DownloadContentType: dl.ContentType,
				DownloadUrl:         dlin.Url,
				PHash:               phash,
				FileSize:            rz.FileSize,
				Height:              rz.Height,
				Width:               rz.Width,
				ContentType:         rz.ContentType,
				DestinationUrl:      ul.Url,
				Error:               result.Error(),
				Context:             result.Context,
			}

			p.complete <- r
		}
	}
	close(p.complete)
	p.outstanding.Done()
}

func (p *ImageProcessor) Completed() <-chan *ImageProcessorOutput {
	return p.complete
}

type ImageProcessorOutput struct {
	DownloadSize        int64
	DownloadContentType string
	DownloadUrl         string
	PHash               uint64

	FileSize    int64
	Height      int
	Width       int
	ContentType string

	DestinationUrl string
	Error          error
	Context        *DataContext
}
<commit_msg>update<commit_after>package multi

import (
	"os"
	"path"
	"sync"

	"github.com/gotgo/fw/logging"
)

type ImageProcessor struct {

	// Uploader - required
	Uploader Uploader

	// LocalPath - defaults to os.TempDir()/downloads
	LocalPath string

	Log logging.Logger

	MaxHeight int
	MaxWidth  int

	downloader *TaskRun
	phasher    *TaskRun
	resizer    *TaskRun
	uploader   *TaskRun

	outstanding sync.WaitGroup
	complete    chan *ImageProcessorOutput
}

func (ip *ImageProcessor) setup() {
	uploader := ip.Uploader
	if uploader == nil {
		panic("uploader not set")
	}

	if ip.Log == nil {
		ip.Log = &logging.NoOpLogger{}
	}

	var tempFolder = ip.LocalPath
	if tempFolder == "" {
		tempFolder = path.Join(os.TempDir(), "downloads")
	}

	os.RemoveAll(tempFolder)
	os.MkdirAll(tempFolder, 0777)

	ip.complete = make(chan *ImageProcessorOutput, 100)

	ip.downloader = &TaskRun{
		Action:       &FileDownloadTask{Folder: tempFolder},
		Concurrency:  10,
		MaxQueuedIn:  10 * 5,
		MaxQueuedOut: 10 * 10,
	}

	ip.phasher = &TaskRun{
		Action: &PHashTask{
			Log: ip.Log,
		},
		Concurrency:  2,
		MaxQueuedIn:  2,
		MaxQueuedOut: 100,
	}

	ip.resizer = &TaskRun{
		Action:       &ResizeImageTask{MaxHeight: ip.MaxHeight, MaxWidth: ip.MaxWidth},
		Concurrency:  6,
		MaxQueuedIn:  12,
		MaxQueuedOut: 100,
	}

	ip.uploader = &TaskRun{
		Action:       &FileUploadTask{Uploader: uploader},
		Concurrency:  8,
		MaxQueuedIn:  8 * 10,
		MaxQueuedOut: 100,
	}
}

func (p *ImageProcessor) Startup() {
	p.setup()
	p.doScavenge()
}

func (p *ImageProcessor) Shutdown() {
	p.downloader.Shutdown()
	p.outstanding.Wait()
}

func (p *ImageProcessor) Injest(url, filename string, ctx *DataContext) {
	in := &FileDownloadInput{
		Url:      url,
		Filename: filename,
	}

	p.downloader.Add(in, ctx)
}

func (p *ImageProcessor) doScavenge() {
	p.downloader.Startup()
	p.phasher.Startup()
	p.resizer.Startup()
	p.uploader.Startup()

	p.outstanding.Add(1)
	// phash
	go p.phash()
	p.outstanding.Add(1)
	// resize
	go p.resize()
	p.outstanding.Add(1)
	// upload
	go p.upload()
	p.outstanding.Add(1)
	// collector
	go p.wrapUp()
}

func (p *ImageProcessor) handleError(message string, result *TaskRunOutput) {
	p.Log.Error(message, result.Error())
	p.complete <- &ImageProcessorOutput{
		Error:   result.Error(),
		Context: result.Context,
	}
}

func (p *ImageProcessor) phash() {
	for dl := range p.downloader.Completed() {
		if dl.Error() != nil {
			p.handleError("download failed", dl)
		} else {
			result := dl.Context.Get(p.downloader.Name()).(*TaskRunResult)
			dlo := result.Output.(*FileDownloadOutput)
			p.phasher.Add(dlo.Path, dl.Context)
		}
	}
	p.phasher.Shutdown()
	p.outstanding.Done()
}

func (p *ImageProcessor) resize() {
	for ph := range p.phasher.Completed() {
		if ph.Error() != nil {
			p.handleError("Phash failed", ph)
		} else {
			result := ph.Context.Get(p.downloader.Name()).(*TaskRunResult)
			dlo := result.Output.(*FileDownloadOutput)
			p.resizer.Add(dlo.Path, ph.Context)
		}
	}
	p.resizer.Shutdown()
	p.outstanding.Done()
}

func (p *ImageProcessor) upload() {
	for rz := range p.resizer.Completed() {
		if rz.Error() != nil {
			p.handleError("resize failed", rz)
		} else {
			rzOut := rz.Output().(*ImageResizeOutput)
			p.uploader.Add(rzOut.FilePath, rz.Context)
		}
	}
	p.uploader.Shutdown()
	p.outstanding.Done()
}

func (p *ImageProcessor) wrapUp() {
	for result := range p.uploader.Completed() {
		if result.Error() != nil {
			p.handleError("upload failed", result)
		} else {
			dl := result.Previous(p.downloader.Name()).Output.(*FileDownloadOutput)
			dlin := result.Previous(p.downloader.Name()).Input.(*FileDownloadInput)
			rz := result.Previous(p.resizer.Name()).Output.(*ImageResizeOutput)
			phash := result.Previous(p.phasher.Name()).Output.(uint64)
			ul := result.Previous(p.uploader.Name()).Output.(*FileUploadOutput)

			if dl == nil || dlin == nil {
				panic("nil - download in or out")
			}
			if rz == nil {
				panic("nil - resizer")
			}
			if ul == nil {
				panic("nil - upload")
			}
			if result == nil {
				panic("nil - result")
			}

			r := &ImageProcessorOutput{
				DownloadSize:        dl.Size,
				DownloadContentType: dl.ContentType,
				DownloadUrl:         dlin.Url,
				PHash:               phash,
				FileSize:            rz.FileSize,
				Height:              rz.Height,
				Width:               rz.Width,
				ContentType:         rz.ContentType,
				DestinationUrl:      ul.Url,
				Error:               result.Error(),
				Context:             result.Context,
			}

			p.complete <- r
		}
	}
	close(p.complete)
	p.outstanding.Done()
}

func (p *ImageProcessor) Completed() <-chan *ImageProcessorOutput {
	return p.complete
}

type ImageProcessorOutput struct {
	DownloadSize        int64
	DownloadContentType string
	DownloadUrl         string
	PHash               uint64

	FileSize    int64
	Height      int
	Width       int
	ContentType string

	DestinationUrl string
	Error          error
	Context        *DataContext
}
<|endoftext|>


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

package hugofs

import (
	"github.com/spf13/afero"
	"github.com/spf13/viper"
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestInitDefault(t *testing.T) {
	viper.Reset()
	defer viper.Reset()

	InitDefaultFs()

	assert.IsType(t, new(afero.OsFs), Source())
	assert.IsType(t, new(afero.OsFs), Destination())
	assert.IsType(t, new(afero.OsFs), Os())
	assert.Nil(t, WorkingDir())
}

func TestInitMemFs(t *testing.T) {
	viper.Reset()
	defer viper.Reset()

	InitMemFs()

	assert.IsType(t, new(afero.MemMapFs), Source())
	assert.IsType(t, new(afero.MemMapFs), Destination())
	assert.IsType(t, new(afero.OsFs), Os())
	assert.Nil(t, WorkingDir())
}

func TestSetSource(t *testing.T) {

	InitMemFs()

	SetSource(new(afero.OsFs))
	assert.IsType(t, new(afero.OsFs), Source())
}

func TestSetDestination(t *testing.T) {

	InitMemFs()

	SetDestination(new(afero.OsFs))
	assert.IsType(t, new(afero.OsFs), Destination())
}

func TestWorkingDir(t *testing.T) {
	viper.Reset()
	defer viper.Reset()

	viper.Set("WorkingDir", "/a/b/")

	InitMemFs()

	assert.IsType(t, new(afero.BasePathFs), WorkingDir())
}
<commit_msg>hugofs: Add missing not nil checks to tests<commit_after>// Copyright 2016 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package hugofs

import (
	"github.com/spf13/afero"
	"github.com/spf13/viper"
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestInitDefault(t *testing.T) {
	viper.Reset()
	defer viper.Reset()

	InitDefaultFs()

	assert.NotNil(t, Source())
	assert.IsType(t, new(afero.OsFs), Source())
	assert.NotNil(t, Destination())
	assert.IsType(t, new(afero.OsFs), Destination())
	assert.NotNil(t, Os())
	assert.IsType(t, new(afero.OsFs), Os())
	assert.Nil(t, WorkingDir())
}

func TestInitMemFs(t *testing.T) {
	viper.Reset()
	defer viper.Reset()

	InitMemFs()

	assert.NotNil(t, Source())
	assert.IsType(t, new(afero.MemMapFs), Source())
	assert.NotNil(t, Destination())
	assert.IsType(t, new(afero.MemMapFs), Destination())
	assert.IsType(t, new(afero.OsFs), Os())
	assert.Nil(t, WorkingDir())
}

func TestSetSource(t *testing.T) {

	InitMemFs()

	SetSource(new(afero.OsFs))
	assert.NotNil(t, Source())
	assert.IsType(t, new(afero.OsFs), Source())
}

func TestSetDestination(t *testing.T) {

	InitMemFs()

	SetDestination(new(afero.OsFs))
	assert.NotNil(t, Destination())
	assert.IsType(t, new(afero.OsFs), Destination())
}

func TestWorkingDir(t *testing.T) {
	viper.Reset()
	defer viper.Reset()

	viper.Set("WorkingDir", "/a/b/")

	InitMemFs()

	assert.NotNil(t, WorkingDir())
	assert.IsType(t, new(afero.BasePathFs), WorkingDir())
}
<|endoftext|>


<commit_before>package constants

const (
	SubPortNumber                = 6969
	DominatorPortNumber          = 6970
	ImageServerPortNumber        = 6971
	BasicFileGenServerPortNumber = 6972
	SimpleMdbServerPortNumber    = 6973
	ImageUnpackerPortNumber      = 6974

	DefaultCpuPercent          = 50
	DefaultNetworkSpeedPercent = 10
	DefaultScanSpeedPercent    = 2

	AssignedOIDBase        = "1.3.6.1.4.1.9586.100.7"
	PermittedMethodListOID = AssignedOIDBase + ".1"
)

var RequiredPaths = map[string]rune{
	"/etc":        'd',
	"/etc/passwd": 'f',
	"/usr":        'd',
	"/usr/bin":    'd',
}

var ScanExcludeList = []string{
	"/home/.*",
	"/tmp/.*",
	"/var/log/.*",
	"/var/mail/.*",
	"/var/spool/.*",
	"/var/tmp/.*",
}
<commit_msg>Add lib/constants.ImaginatorPortNumber.<commit_after>package constants

const (
	SubPortNumber                = 6969
	DominatorPortNumber          = 6970
	ImageServerPortNumber        = 6971
	BasicFileGenServerPortNumber = 6972
	SimpleMdbServerPortNumber    = 6973
	ImageUnpackerPortNumber      = 6974
	ImaginatorPortNumber         = 6975

	DefaultCpuPercent          = 50
	DefaultNetworkSpeedPercent = 10
	DefaultScanSpeedPercent    = 2

	AssignedOIDBase        = "1.3.6.1.4.1.9586.100.7"
	PermittedMethodListOID = AssignedOIDBase + ".1"
)

var RequiredPaths = map[string]rune{
	"/etc":        'd',
	"/etc/passwd": 'f',
	"/usr":        'd',
	"/usr/bin":    'd',
}

var ScanExcludeList = []string{
	"/home/.*",
	"/tmp/.*",
	"/var/log/.*",
	"/var/mail/.*",
	"/var/spool/.*",
	"/var/tmp/.*",
}
<|endoftext|>


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

package types

import (
	"time"
)

type MetricPoint struct {
	Timestamp time.Time `json:"timestamp"`
	Value     uint64    `json:"value"`
}

type MetricResult struct {
	Metrics         []MetricPoint `json:"metrics"`
	LatestTimestamp time.Time     `json:"latestTimestamp"`
}

type MetricResultList struct {
	Items []MetricResult `json:"items"`
}

type Stats struct {
	Average     uint64 `json:"average"`
	NinetyFifth uint64 `json:"percentile"`
	Max         uint64 `json:"max"`
}

type ExternalStatBundle struct {
	Minute Stats `json:"minute"`
	Hour   Stats `json:"hour"`
	Day    Stats `json:"day"`
}

type StatsResponse struct {
	// Uptime is in seconds
	Uptime uint64                        `json:"uptime"`
	Stats  map[string]ExternalStatBundle `json:"stats"`
}

// An ExternalEntityListEntry represents the latest CPU and Memory usage of a model entity.
// A model entity can be a Pod, a Container, a Namespace or a Node.
type ExternalEntityListEntry struct {
	Name     string `json:"name"`
	CPUUsage uint64 `json:"cpuUsage"`
	MemUsage uint64 `json:"memUsage"`
}
<commit_msg>Add float to heapster model api [hs]<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package types

import (
	"time"
)

type MetricPoint struct {
	Timestamp time.Time `json:"timestamp"`
	Value     uint64    `json:"value"`
	// This will be populated only for float custom metrics. In that case
	// "value" will be zero. This is a temporary hack. Overall most likely
	// we will need a new api versioned in the similar way as K8S api.
	FloatValue *float64 `json:"floatValue,omitempty"`
}

type MetricResult struct {
	Metrics         []MetricPoint `json:"metrics"`
	LatestTimestamp time.Time     `json:"latestTimestamp"`
}

type MetricResultList struct {
	Items []MetricResult `json:"items"`
}

type Stats struct {
	Average     uint64 `json:"average"`
	NinetyFifth uint64 `json:"percentile"`
	Max         uint64 `json:"max"`
}

type ExternalStatBundle struct {
	Minute Stats `json:"minute"`
	Hour   Stats `json:"hour"`
	Day    Stats `json:"day"`
}

type StatsResponse struct {
	// Uptime is in seconds
	Uptime uint64                        `json:"uptime"`
	Stats  map[string]ExternalStatBundle `json:"stats"`
}

// An ExternalEntityListEntry represents the latest CPU and Memory usage of a model entity.
// A model entity can be a Pod, a Container, a Namespace or a Node.
type ExternalEntityListEntry struct {
	Name     string `json:"name"`
	CPUUsage uint64 `json:"cpuUsage"`
	MemUsage uint64 `json:"memUsage"`
}
<|endoftext|>


<commit_before>package readline

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
)

type AutoCompleter interface {
	// Readline will pass the whole line and current offset to it
	// Completer need to pass all the candidates, and how long they shared the same characters in line
	// Example:
	//   [go, git, git-shell, grep]
	//   Do("g", 1) => ["o", "it", "it-shell", "rep"], 1
	//   Do("gi", 2) => ["t", "t-shell"], 2
	//   Do("git", 3) => ["", "-shell"], 3
	Do(line []rune, pos int) (newLine [][]rune, length int)
}

type TabCompleter struct{}

func (t *TabCompleter) Do([]rune, int) ([][]rune, int) {
	return [][]rune{[]rune("\t")}, 0
}

type opCompleter struct {
	w     io.Writer
	op    *Operation
	width int

	inCompleteMode  bool
	inSelectMode    bool
	candidate       [][]rune
	candidateSource []rune
	candidateOff    int
	candidateChoise int
	candidateColNum int
}

func newOpCompleter(w io.Writer, op *Operation, width int) *opCompleter {
	return &opCompleter{
		w:     w,
		op:    op,
		width: width,
	}
}

func (o *opCompleter) doSelect() {
	if len(o.candidate) == 1 {
		o.op.buf.WriteRunes(o.candidate[0])
		o.ExitCompleteMode(false)
		return
	}
	o.nextCandidate(1)
	o.CompleteRefresh()
}

func (o *opCompleter) nextCandidate(i int) {
	o.candidateChoise += i
	o.candidateChoise = o.candidateChoise % len(o.candidate)
	if o.candidateChoise < 0 {
		o.candidateChoise = len(o.candidate) + o.candidateChoise
	}
}

func (o *opCompleter) OnComplete() bool {
	if o.width == 0 {
		return false
	}
	if o.IsInCompleteSelectMode() {
		o.doSelect()
		return true
	}

	buf := o.op.buf
	rs := buf.Runes()

	if o.IsInCompleteMode() && o.candidateSource != nil && runes.Equal(rs, o.candidateSource) {
		o.EnterCompleteSelectMode()
		o.doSelect()
		return true
	}

	o.ExitCompleteSelectMode()
	o.candidateSource = rs
	newLines, offset := o.op.cfg.AutoComplete.Do(rs, buf.idx)
	if len(newLines) == 0 {
		o.ExitCompleteMode(false)
		return true
	}

	// only Aggregate candidates in non-complete mode
	if !o.IsInCompleteMode() {
		if len(newLines) == 1 {
			buf.WriteRunes(newLines[0])
			o.ExitCompleteMode(false)
			return true
		}

		same, size := runes.Aggregate(newLines)
		if size > 0 {
			buf.WriteRunes(same)
			o.ExitCompleteMode(false)
			return true
		}
	}

	o.EnterCompleteMode(offset, newLines)
	return true
}

func (o *opCompleter) IsInCompleteSelectMode() bool {
	return o.inSelectMode
}

func (o *opCompleter) IsInCompleteMode() bool {
	return o.inCompleteMode
}

func (o *opCompleter) HandleCompleteSelect(r rune) bool {
	next := true
	switch r {
	case CharEnter, CharCtrlJ:
		next = false
		o.op.buf.WriteRunes(o.op.candidate[o.op.candidateChoise])
		o.ExitCompleteMode(false)
	case CharLineStart:
		num := o.candidateChoise % o.candidateColNum
		o.nextCandidate(-num)
	case CharLineEnd:
		num := o.candidateColNum - o.candidateChoise%o.candidateColNum - 1
		o.candidateChoise += num
		if o.candidateChoise >= len(o.candidate) {
			o.candidateChoise = len(o.candidate) - 1
		}
	case CharBackspace:
		o.ExitCompleteSelectMode()
		next = false
	case CharTab, CharForward:
		o.doSelect()
	case CharBell, CharInterrupt:
		o.ExitCompleteMode(true)
		next = false
	case CharNext:
		tmpChoise := o.candidateChoise + o.candidateColNum
		if tmpChoise >= o.getMatrixSize() {
			tmpChoise -= o.getMatrixSize()
		} else if tmpChoise >= len(o.candidate) {
			tmpChoise += o.candidateColNum
			tmpChoise -= o.getMatrixSize()
		}
		o.candidateChoise = tmpChoise
	case CharBackward:
		o.nextCandidate(-1)
	case CharPrev:
		tmpChoise := o.candidateChoise - o.candidateColNum
		if tmpChoise < 0 {
			tmpChoise += o.getMatrixSize()
			if tmpChoise >= len(o.candidate) {
				tmpChoise -= o.candidateColNum
			}
		}
		o.candidateChoise = tmpChoise
	default:
		next = false
		o.ExitCompleteSelectMode()
	}
	if next {
		o.CompleteRefresh()
		return true
	}
	return false
}

func (o *opCompleter) getMatrixSize() int {
	line := len(o.candidate) / o.candidateColNum
	if len(o.candidate)%o.candidateColNum != 0 {
		line++
	}
	return line * o.candidateColNum
}

func (o *opCompleter) OnWidthChange(newWidth int) {
	o.width = newWidth
}

func (o *opCompleter) CompleteRefresh() {
	if !o.inCompleteMode {
		return
	}
	lineCnt := o.op.buf.CursorLineCount()
	colWidth := 0
	for _, c := range o.candidate {
		w := runes.WidthAll(c)
		if w > colWidth {
			colWidth = w
		}
	}
	colWidth += o.candidateOff + 1
	same := o.op.buf.RuneSlice(-o.candidateOff)

	// -1 to avoid reach the end of line
	width := o.width - 1
	colNum := width / colWidth
	colWidth += (width - (colWidth * colNum)) / colNum

	o.candidateColNum = colNum
	buf := bufio.NewWriter(o.w)
	buf.Write(bytes.Repeat([]byte("\n"), lineCnt))

	colIdx := 0
	lines := 1
	buf.WriteString("\033[J")
	for idx, c := range o.candidate {
		inSelect := idx == o.candidateChoise && o.IsInCompleteSelectMode()
		if inSelect {
			buf.WriteString("\033[30;47m")
		}
		buf.WriteString(string(same))
		buf.WriteString(string(c))
		buf.Write(bytes.Repeat([]byte(" "), colWidth-len(c)-len(same)))

		if inSelect {
			buf.WriteString("\033[0m")
		}

		colIdx++
		if colIdx == colNum {
			buf.WriteString("\n")
			lines++
			colIdx = 0
		}
	}

	// move back
	fmt.Fprintf(buf, "\033[%dA\r", lineCnt-1+lines)
	fmt.Fprintf(buf, "\033[%dC", o.op.buf.idx+o.op.buf.PromptLen())
	buf.Flush()
}

func (o *opCompleter) aggCandidate(candidate [][]rune) int {
	offset := 0
	for i := 0; i < len(candidate[0]); i++ {
		for j := 0; j < len(candidate)-1; j++ {
			if i > len(candidate[j]) {
				goto aggregate
			}
			if candidate[j][i] != candidate[j+1][i] {
				goto aggregate
			}
		}
		offset = i
	}
aggregate:
	return offset
}

func (o *opCompleter) EnterCompleteSelectMode() {
	o.inSelectMode = true
	o.candidateChoise = -1
	o.CompleteRefresh()
}

func (o *opCompleter) EnterCompleteMode(offset int, candidate [][]rune) {
	o.inCompleteMode = true
	o.candidate = candidate
	o.candidateOff = offset
	o.CompleteRefresh()
}

func (o *opCompleter) ExitCompleteSelectMode() {
	o.inSelectMode = false
	o.candidate = nil
	o.candidateChoise = -1
	o.candidateOff = -1
	o.candidateSource = nil
}

func (o *opCompleter) ExitCompleteMode(revent bool) {
	o.inCompleteMode = false
	o.ExitCompleteSelectMode()
}
<commit_msg>fix readline complete update<commit_after>package readline

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
)

type AutoCompleter interface {
	// Readline will pass the whole line and current offset to it
	// Completer need to pass all the candidates, and how long they shared the same characters in line
	// Example:
	//   [go, git, git-shell, grep]
	//   Do("g", 1) => ["o", "it", "it-shell", "rep"], 1
	//   Do("gi", 2) => ["t", "t-shell"], 2
	//   Do("git", 3) => ["", "-shell"], 3
	Do(line []rune, pos int) (newLine [][]rune, length int)
}

type TabCompleter struct{}

func (t *TabCompleter) Do([]rune, int) ([][]rune, int) {
	return [][]rune{[]rune("\t")}, 0
}

type opCompleter struct {
	w     io.Writer
	op    *Operation
	width int

	inCompleteMode  bool
	inSelectMode    bool
	candidate       [][]rune
	candidateSource []rune
	candidateOff    int
	candidateChoise int
	candidateColNum int
}

func newOpCompleter(w io.Writer, op *Operation, width int) *opCompleter {
	return &opCompleter{
		w:     w,
		op:    op,
		width: width,
	}
}

func (o *opCompleter) doSelect() {
	if len(o.candidate) == 1 {
		o.op.buf.WriteRunes(o.candidate[0])
		o.ExitCompleteMode(false)
		return
	}
	o.nextCandidate(1)
	o.CompleteRefresh()
}

func (o *opCompleter) nextCandidate(i int) {
	o.candidateChoise += i
	o.candidateChoise = o.candidateChoise % len(o.candidate)
	if o.candidateChoise < 0 {
		o.candidateChoise = len(o.candidate) + o.candidateChoise
	}
}

func (o *opCompleter) OnComplete() bool {
	if o.width == 0 {
		return false
	}
	if o.IsInCompleteSelectMode() {
		o.doSelect()
		return true
	}

	buf := o.op.buf
	rs := buf.Runes()

	if o.IsInCompleteMode() && o.candidateSource != nil && runes.Equal(rs, o.candidateSource) {
		o.EnterCompleteSelectMode()
		o.doSelect()
		return true
	}

	o.ExitCompleteSelectMode()
	o.candidateSource = rs
	newLines, offset := o.op.cfg.AutoComplete.Do(rs, buf.idx)
	if len(newLines) == 0 {
		o.ExitCompleteMode(false)
		return true
	}

	// only Aggregate candidates in non-complete mode
	if !o.IsInCompleteMode() {
		if len(newLines) == 1 {
			if offset > 0 {
				buf.Set([]rune(string(buf.Runes())[0:offset] + string(newLines[0])))
			} else {
				buf.WriteRunes(newLines[0])
			}

			o.ExitCompleteMode(false)
			return true
		}

		same, size := runes.Aggregate(newLines)
		if size > 0 {
			buf.WriteRunes(same)
			o.ExitCompleteMode(false)
			return true
		}
	}

	o.EnterCompleteMode(offset, newLines)
	return true
}

func (o *opCompleter) IsInCompleteSelectMode() bool {
	return o.inSelectMode
}

func (o *opCompleter) IsInCompleteMode() bool {
	return o.inCompleteMode
}

func (o *opCompleter) HandleCompleteSelect(r rune) bool {
	next := true
	switch r {
	case CharEnter, CharCtrlJ:
		next = false
		o.op.buf.WriteRunes(o.op.candidate[o.op.candidateChoise])
		o.ExitCompleteMode(false)
	case CharLineStart:
		num := o.candidateChoise % o.candidateColNum
		o.nextCandidate(-num)
	case CharLineEnd:
		num := o.candidateColNum - o.candidateChoise%o.candidateColNum - 1
		o.candidateChoise += num
		if o.candidateChoise >= len(o.candidate) {
			o.candidateChoise = len(o.candidate) - 1
		}
	case CharBackspace:
		o.ExitCompleteSelectMode()
		next = false
	case CharTab, CharForward:
		o.doSelect()
	case CharBell, CharInterrupt:
		o.ExitCompleteMode(true)
		next = false
	case CharNext:
		tmpChoise := o.candidateChoise + o.candidateColNum
		if tmpChoise >= o.getMatrixSize() {
			tmpChoise -= o.getMatrixSize()
		} else if tmpChoise >= len(o.candidate) {
			tmpChoise += o.candidateColNum
			tmpChoise -= o.getMatrixSize()
		}
		o.candidateChoise = tmpChoise
	case CharBackward:
		o.nextCandidate(-1)
	case CharPrev:
		tmpChoise := o.candidateChoise - o.candidateColNum
		if tmpChoise < 0 {
			tmpChoise += o.getMatrixSize()
			if tmpChoise >= len(o.candidate) {
				tmpChoise -= o.candidateColNum
			}
		}
		o.candidateChoise = tmpChoise
	default:
		next = false
		o.ExitCompleteSelectMode()
	}
	if next {
		o.CompleteRefresh()
		return true
	}
	return false
}

func (o *opCompleter) getMatrixSize() int {
	line := len(o.candidate) / o.candidateColNum
	if len(o.candidate)%o.candidateColNum != 0 {
		line++
	}
	return line * o.candidateColNum
}

func (o *opCompleter) OnWidthChange(newWidth int) {
	o.width = newWidth
}

func (o *opCompleter) CompleteRefresh() {
	if !o.inCompleteMode {
		return
	}
	lineCnt := o.op.buf.CursorLineCount()
	colWidth := 0
	for _, c := range o.candidate {
		w := runes.WidthAll(c)
		if w > colWidth {
			colWidth = w
		}
	}
	colWidth += o.candidateOff + 1
	same := o.op.buf.RuneSlice(-o.candidateOff)

	// -1 to avoid reach the end of line
	width := o.width - 1
	colNum := width / colWidth
	colWidth += (width - (colWidth * colNum)) / colNum

	o.candidateColNum = colNum
	buf := bufio.NewWriter(o.w)
	buf.Write(bytes.Repeat([]byte("\n"), lineCnt))

	colIdx := 0
	lines := 1
	buf.WriteString("\033[J")
	for idx, c := range o.candidate {
		inSelect := idx == o.candidateChoise && o.IsInCompleteSelectMode()
		if inSelect {
			buf.WriteString("\033[30;47m")
		}
		buf.WriteString(string(same))
		buf.WriteString(string(c))
		buf.Write(bytes.Repeat([]byte(" "), colWidth-len(c)-len(same)))

		if inSelect {
			buf.WriteString("\033[0m")
		}

		colIdx++
		if colIdx == colNum {
			buf.WriteString("\n")
			lines++
			colIdx = 0
		}
	}

	// move back
	fmt.Fprintf(buf, "\033[%dA\r", lineCnt-1+lines)
	fmt.Fprintf(buf, "\033[%dC", o.op.buf.idx+o.op.buf.PromptLen())
	buf.Flush()
}

func (o *opCompleter) aggCandidate(candidate [][]rune) int {
	offset := 0
	for i := 0; i < len(candidate[0]); i++ {
		for j := 0; j < len(candidate)-1; j++ {
			if i > len(candidate[j]) {
				goto aggregate
			}
			if candidate[j][i] != candidate[j+1][i] {
				goto aggregate
			}
		}
		offset = i
	}
aggregate:
	return offset
}

func (o *opCompleter) EnterCompleteSelectMode() {
	o.inSelectMode = true
	o.candidateChoise = -1
	o.CompleteRefresh()
}

func (o *opCompleter) EnterCompleteMode(offset int, candidate [][]rune) {
	o.inCompleteMode = true
	o.candidate = candidate
	o.candidateOff = offset
	o.CompleteRefresh()
}

func (o *opCompleter) ExitCompleteSelectMode() {
	o.inSelectMode = false
	o.candidate = nil
	o.candidateChoise = -1
	o.candidateOff = -1
	o.candidateSource = nil
}

func (o *opCompleter) ExitCompleteMode(revent bool) {
	o.inCompleteMode = false
	o.ExitCompleteSelectMode()
}
<|endoftext|>


<commit_before>package tweet_feature

import (
	"fmt"
	"regexp"

	"github.com/syou6162/go-active-learning/lib/feature"
	"github.com/syou6162/go-active-learning/lib/model"
	"gopkg.in/vmarkovtsev/go-lcss.v1"
)

type ExampleAndTweet struct {
	example     *model.Example
	tweet       *model.Tweet
	lcsLen      int
	atMarksCnt  int
	hashTagsCnt int
	cleanedText string
}

func GetExampleAndTweet(e *model.Example, t *model.Tweet) ExampleAndTweet {
	result := ExampleAndTweet{example: e, tweet: t}
	result.lcsLen = GetLCSLen(result)

	atRegexp := regexp.MustCompile(`@[^/]+`)
	result.atMarksCnt = len(atRegexp.FindAllStringSubmatch(t.FullText, -1))
	str := atRegexp.ReplaceAllString(t.FullText, " ")
	hashRegexp := regexp.MustCompile(`#[^/]+`)
	result.hashTagsCnt = len(hashRegexp.FindAllStringSubmatch(t.FullText, -1))
	result.cleanedText = hashRegexp.ReplaceAllString(str, " ")
	return result
}

func GetLCSLen(et ExampleAndTweet) int {
	return len(string(lcss.LongestCommonSubstring([]byte(et.example.Title), []byte(et.tweet.FullText))))
}

func LCSLenFeature(et ExampleAndTweet) string {
	prefix := "LCSLenFeature"
	switch {
	case et.lcsLen == 0:
		return fmt.Sprintf("%s:0", prefix)
	case et.lcsLen < 5:
		return fmt.Sprintf("%s:5", prefix)
	case et.lcsLen < 10:
		return fmt.Sprintf("%s:10", prefix)
	case et.lcsLen < 25:
		return fmt.Sprintf("%s:25", prefix)
	case et.lcsLen < 50:
		return fmt.Sprintf("%s:50", prefix)
	case et.lcsLen < 100:
		return fmt.Sprintf("%s:100", prefix)
	default:
		return fmt.Sprintf("%s:INF", prefix)
	}
}

func LCSRatioFeature(et ExampleAndTweet) string {
	prefix := "LCSRatioFeature"
	ratio := float64(et.lcsLen) / float64(len(et.tweet.FullText))
	switch {
	case ratio == 0.0:
		return fmt.Sprintf("%s:0.0", prefix)
	case ratio < 0.1:
		return fmt.Sprintf("%s:0.1", prefix)
	case ratio < 0.25:
		return fmt.Sprintf("%s:0.25", prefix)
	case ratio < 0.5:
		return fmt.Sprintf("%s:0.5", prefix)
	case ratio < 0.75:
		return fmt.Sprintf("%s:0.75", prefix)
	case ratio < 0.9:
		return fmt.Sprintf("%s:0.0", prefix)
	default:
		return fmt.Sprintf("%s:1.0", prefix)
	}
}

func FavoriteCountFeature(et ExampleAndTweet) string {
	prefix := "FavoriteCountFeature"
	cnt := et.tweet.FavoriteCount
	switch {
	case cnt == 0:
		return fmt.Sprintf("%s:0", prefix)
	case cnt == 1:
		return fmt.Sprintf("%s:1", prefix)
	case cnt == 3:
		return fmt.Sprintf("%s:3", prefix)
	case cnt < 5:
		return fmt.Sprintf("%s:5", prefix)
	case cnt < 10:
		return fmt.Sprintf("%s:10", prefix)
	case cnt < 25:
		return fmt.Sprintf("%s:25", prefix)
	case cnt < 50:
		return fmt.Sprintf("%s:50", prefix)
	case cnt < 100:
		return fmt.Sprintf("%s:100", prefix)
	default:
		return fmt.Sprintf("%s:INF", prefix)
	}
}

func RetweetCountFeature(et ExampleAndTweet) string {
	prefix := "RetweetCountFeature"
	cnt := et.tweet.RetweetCount
	switch {
	case cnt == 0:
		return fmt.Sprintf("%s:0", prefix)
	case cnt == 1:
		return fmt.Sprintf("%s:1", prefix)
	case cnt == 3:
		return fmt.Sprintf("%s:3", prefix)
	case cnt < 5:
		return fmt.Sprintf("%s:5", prefix)
	case cnt < 10:
		return fmt.Sprintf("%s:10", prefix)
	case cnt < 25:
		return fmt.Sprintf("%s:25", prefix)
	case cnt < 50:
		return fmt.Sprintf("%s:50", prefix)
	case cnt < 100:
		return fmt.Sprintf("%s:100", prefix)
	default:
		return fmt.Sprintf("%s:INF", prefix)
	}
}

func AtMarksCountFeature(et ExampleAndTweet) string {
	prefix := "AtMarksCountFeature"
	cnt := et.atMarksCnt
	switch {
	case cnt == 0:
		return fmt.Sprintf("%s:0", prefix)
	case cnt == 1:
		return fmt.Sprintf("%s:1", prefix)
	case cnt == 3:
		return fmt.Sprintf("%s:3", prefix)
	case cnt < 5:
		return fmt.Sprintf("%s:5", prefix)
	case cnt < 10:
		return fmt.Sprintf("%s:10", prefix)
	default:
		return fmt.Sprintf("%s:INF", prefix)
	}
}

func HashTagsCountFeature(et ExampleAndTweet) string {
	prefix := "HashTagsCountFeature"
	cnt := et.atMarksCnt
	switch {
	case cnt == 0:
		return fmt.Sprintf("%s:0", prefix)
	case cnt == 1:
		return fmt.Sprintf("%s:1", prefix)
	case cnt == 3:
		return fmt.Sprintf("%s:3", prefix)
	case cnt < 5:
		return fmt.Sprintf("%s:5", prefix)
	case cnt < 10:
		return fmt.Sprintf("%s:10", prefix)
	default:
		return fmt.Sprintf("%s:INF", prefix)
	}
}

func ScreenNameFeature(et ExampleAndTweet) string {
	prefix := "ScreenNameFeature"
	return fmt.Sprintf("%s:%s", prefix, et.tweet.ScreenName)
}

func GetTweetFeature(e *model.Example, t *model.Tweet) feature.FeatureVector {
	var fv feature.FeatureVector
	et := GetExampleAndTweet(e, t)

	fv = append(fv, "BIAS")
	fv = append(fv, LCSLenFeature(et))
	fv = append(fv, LCSRatioFeature(et))
	fv = append(fv, ScreenNameFeature(et))
	fv = append(fv, FavoriteCountFeature(et))
	fv = append(fv, RetweetCountFeature(et))
	fv = append(fv, AtMarksCountFeature(et))
	fv = append(fv, HashTagsCountFeature(et))
	return fv
}
<commit_msg>綺麗にした後の長さも取れるように<commit_after>package tweet_feature

import (
	"fmt"
	"regexp"

	"github.com/syou6162/go-active-learning/lib/feature"
	"github.com/syou6162/go-active-learning/lib/model"
	"gopkg.in/vmarkovtsev/go-lcss.v1"
)

type ExampleAndTweet struct {
	example       *model.Example
	tweet         *model.Tweet
	lcsLen        int
	atMarksCnt    int
	hashTagsCnt   int
	cleanedText   string
	cleanedLcsLen int
}

func GetExampleAndTweet(e *model.Example, t *model.Tweet) ExampleAndTweet {
	result := ExampleAndTweet{example: e, tweet: t}
	result.lcsLen = GetLCSLen(e.Title, t.FullText)

	atRegexp := regexp.MustCompile(`@[^ ]+`)
	result.atMarksCnt = len(atRegexp.FindAllStringSubmatch(t.FullText, -1))
	str := atRegexp.ReplaceAllString(t.FullText, "")
	hashRegexp := regexp.MustCompile(`#[^ ]+`)
	result.hashTagsCnt = len(hashRegexp.FindAllStringSubmatch(t.FullText, -1))
	result.cleanedText = hashRegexp.ReplaceAllString(str, "")
	result.cleanedLcsLen = GetLCSLen(e.Title, result.cleanedText)
	return result
}

func GetLCSLen(str1 string, str2 string) int {
	return len(string(lcss.LongestCommonSubstring([]byte(str1), []byte(str2))))
}

func LCSLenFeature(et ExampleAndTweet) string {
	prefix := "LCSLenFeature"
	len := et.lcsLen
	switch {
	case len == 0:
		return fmt.Sprintf("%s:0", prefix)
	case len < 5:
		return fmt.Sprintf("%s:5", prefix)
	case len < 10:
		return fmt.Sprintf("%s:10", prefix)
	case len < 25:
		return fmt.Sprintf("%s:25", prefix)
	case len < 50:
		return fmt.Sprintf("%s:50", prefix)
	case len < 100:
		return fmt.Sprintf("%s:100", prefix)
	default:
		return fmt.Sprintf("%s:INF", prefix)
	}
}

func CleanedLCSLenFeature(et ExampleAndTweet) string {
	prefix := "CleanedLCSLenFeature"
	len := et.cleanedLcsLen
	switch {
	case len == 0:
		return fmt.Sprintf("%s:0", prefix)
	case len < 5:
		return fmt.Sprintf("%s:5", prefix)
	case len < 10:
		return fmt.Sprintf("%s:10", prefix)
	case len < 25:
		return fmt.Sprintf("%s:25", prefix)
	case len < 50:
		return fmt.Sprintf("%s:50", prefix)
	case len < 100:
		return fmt.Sprintf("%s:100", prefix)
	default:
		return fmt.Sprintf("%s:INF", prefix)
	}
}

func LCSRatioFeature(et ExampleAndTweet) string {
	prefix := "LCSRatioFeature"
	ratio := float64(et.lcsLen) / float64(len(et.tweet.FullText))
	switch {
	case ratio == 0.0:
		return fmt.Sprintf("%s:0.0", prefix)
	case ratio < 0.1:
		return fmt.Sprintf("%s:0.1", prefix)
	case ratio < 0.25:
		return fmt.Sprintf("%s:0.25", prefix)
	case ratio < 0.5:
		return fmt.Sprintf("%s:0.5", prefix)
	case ratio < 0.75:
		return fmt.Sprintf("%s:0.75", prefix)
	case ratio < 0.9:
		return fmt.Sprintf("%s:0.0", prefix)
	default:
		return fmt.Sprintf("%s:1.0", prefix)
	}
}

func CleanedLCSRatioFeature(et ExampleAndTweet) string {
	prefix := "CleanedLCSRatioFeature"
	ratio := float64(et.cleanedLcsLen) / float64(len(et.tweet.FullText))
	switch {
	case ratio == 0.0:
		return fmt.Sprintf("%s:0.0", prefix)
	case ratio < 0.1:
		return fmt.Sprintf("%s:0.1", prefix)
	case ratio < 0.25:
		return fmt.Sprintf("%s:0.25", prefix)
	case ratio < 0.5:
		return fmt.Sprintf("%s:0.5", prefix)
	case ratio < 0.75:
		return fmt.Sprintf("%s:0.75", prefix)
	case ratio < 0.9:
		return fmt.Sprintf("%s:0.0", prefix)
	default:
		return fmt.Sprintf("%s:1.0", prefix)
	}
}

func FavoriteCountFeature(et ExampleAndTweet) string {
	prefix := "FavoriteCountFeature"
	cnt := et.tweet.FavoriteCount
	switch {
	case cnt == 0:
		return fmt.Sprintf("%s:0", prefix)
	case cnt == 1:
		return fmt.Sprintf("%s:1", prefix)
	case cnt <= 3:
		return fmt.Sprintf("%s:3", prefix)
	case cnt <= 5:
		return fmt.Sprintf("%s:5", prefix)
	case cnt <= 10:
		return fmt.Sprintf("%s:10", prefix)
	case cnt <= 25:
		return fmt.Sprintf("%s:25", prefix)
	case cnt <= 50:
		return fmt.Sprintf("%s:50", prefix)
	case cnt <= 100:
		return fmt.Sprintf("%s:100", prefix)
	default:
		return fmt.Sprintf("%s:INF", prefix)
	}
}

func RetweetCountFeature(et ExampleAndTweet) string {
	prefix := "RetweetCountFeature"
	cnt := et.tweet.RetweetCount
	switch {
	case cnt == 0:
		return fmt.Sprintf("%s:0", prefix)
	case cnt == 1:
		return fmt.Sprintf("%s:1", prefix)
	case cnt <= 3:
		return fmt.Sprintf("%s:3", prefix)
	case cnt <= 5:
		return fmt.Sprintf("%s:5", prefix)
	case cnt <= 10:
		return fmt.Sprintf("%s:10", prefix)
	case cnt <= 25:
		return fmt.Sprintf("%s:25", prefix)
	case cnt <= 50:
		return fmt.Sprintf("%s:50", prefix)
	case cnt <= 100:
		return fmt.Sprintf("%s:100", prefix)
	default:
		return fmt.Sprintf("%s:INF", prefix)
	}
}

func AtMarksCountFeature(et ExampleAndTweet) string {
	prefix := "AtMarksCountFeature"
	cnt := et.atMarksCnt
	switch {
	case cnt == 0:
		return fmt.Sprintf("%s:0", prefix)
	case cnt == 1:
		return fmt.Sprintf("%s:1", prefix)
	case cnt <= 3:
		return fmt.Sprintf("%s:3", prefix)
	case cnt <= 5:
		return fmt.Sprintf("%s:5", prefix)
	case cnt <= 10:
		return fmt.Sprintf("%s:10", prefix)
	default:
		return fmt.Sprintf("%s:INF", prefix)
	}
}

func HashTagsCountFeature(et ExampleAndTweet) string {
	prefix := "HashTagsCountFeature"
	cnt := et.atMarksCnt
	switch {
	case cnt == 0:
		return fmt.Sprintf("%s:0", prefix)
	case cnt == 1:
		return fmt.Sprintf("%s:1", prefix)
	case cnt <= 3:
		return fmt.Sprintf("%s:3", prefix)
	case cnt <= 5:
		return fmt.Sprintf("%s:5", prefix)
	case cnt <= 10:
		return fmt.Sprintf("%s:10", prefix)
	default:
		return fmt.Sprintf("%s:INF", prefix)
	}
}

func TextLengthFeature(et ExampleAndTweet) string {
	prefix := "TextLengthFeature"
	cnt := len(et.tweet.FullText)
	switch {
	case cnt == 0:
		return fmt.Sprintf("%s:0", prefix)
	case cnt == 1:
		return fmt.Sprintf("%s:1", prefix)
	case cnt == 3:
		return fmt.Sprintf("%s:3", prefix)
	case cnt < 5:
		return fmt.Sprintf("%s:5", prefix)
	case cnt < 10:
		return fmt.Sprintf("%s:10", prefix)
	case cnt < 25:
		return fmt.Sprintf("%s:25", prefix)
	case cnt < 50:
		return fmt.Sprintf("%s:50", prefix)
	case cnt < 100:
		return fmt.Sprintf("%s:100", prefix)
	default:
		return fmt.Sprintf("%s:INF", prefix)
	}
}

func CleanedTextLengthFeature(et ExampleAndTweet) string {
	prefix := "CleanedTextLengthFeature"
	cnt := len(et.cleanedText)
	switch {
	case cnt == 0:
		return fmt.Sprintf("%s:0", prefix)
	case cnt == 1:
		return fmt.Sprintf("%s:1", prefix)
	case cnt == 3:
		return fmt.Sprintf("%s:3", prefix)
	case cnt < 5:
		return fmt.Sprintf("%s:5", prefix)
	case cnt < 10:
		return fmt.Sprintf("%s:10", prefix)
	case cnt < 25:
		return fmt.Sprintf("%s:25", prefix)
	case cnt < 50:
		return fmt.Sprintf("%s:50", prefix)
	case cnt < 100:
		return fmt.Sprintf("%s:100", prefix)
	default:
		return fmt.Sprintf("%s:INF", prefix)
	}
}

func ScreenNameFeature(et ExampleAndTweet) string {
	prefix := "ScreenNameFeature"
	return fmt.Sprintf("%s:%s", prefix, et.tweet.ScreenName)
}

func GetTweetFeature(e *model.Example, t *model.Tweet) feature.FeatureVector {
	var fv feature.FeatureVector
	et := GetExampleAndTweet(e, t)

	fv = append(fv, "BIAS")
	fv = append(fv, LCSLenFeature(et))
	fv = append(fv, CleanedLCSLenFeature(et))
	fv = append(fv, LCSRatioFeature(et))
	fv = append(fv, CleanedLCSRatioFeature(et))
	fv = append(fv, TextLengthFeature(et))
	fv = append(fv, CleanedTextLengthFeature(et))

	fv = append(fv, ScreenNameFeature(et))
	fv = append(fv, FavoriteCountFeature(et))
	fv = append(fv, RetweetCountFeature(et))
	fv = append(fv, AtMarksCountFeature(et))
	fv = append(fv, HashTagsCountFeature(et))
	return fv
}
<|endoftext|>


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

package gf256

import "testing"

var f = NewField(0x11d)	// x^8 + x^4 + x^3 + x^2 + 1

func TestBasic(t *testing.T) {
	if f.Exp(0) != 1 || f.Exp(1) != 2 || f.Exp(255) != 1 {
		panic("bad Exp")
	}
}
<commit_msg>gf256: test encoding, linearity, Gauss-Jordan<commit_after>// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gf256

import (
	"bytes"
	"fmt"
	"testing"
)

var f = NewField(0x11d)	// x^8 + x^4 + x^3 + x^2 + 1

func TestBasic(t *testing.T) {
	if f.Exp(0) != 1 || f.Exp(1) != 2 || f.Exp(255) != 1 {
		panic("bad Exp")
	}
}

func TestEncode(t *testing.T) {
	data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11}
	check := []byte{0xa5, 0x24, 0xd4, 0xc1, 0xed, 0x36, 0xc7, 0x87, 0x2c, 0x55}
	out := f.ECBytes(data, len(check))
	if !bytes.Equal(out, check) {
		t.Errorf("have %x want %x", out, check)
	}
}

func TestLinear(t *testing.T) {
	d1 := []byte{0x00, 0x00}
	c1 := []byte{0x00, 0x00}
	if out := f.ECBytes(d1, len(c1)); !bytes.Equal(out, c1) {
		t.Errorf("ECBytes(%x, %d) = %x, want 0", d1, len(c1), out)
	}
	d2 := []byte{0x00, 0x01}
	c2 := f.ECBytes(d2, 2)
	d3 := []byte{0x00, 0x02}
	c3 := f.ECBytes(d3, 2)
	cx := make([]byte, 2)
	for i := range cx {
		cx[i] = c2[i] ^ c3[i]
	}
	d4 := []byte{0x00, 0x03}
	c4 := f.ECBytes(d4, 2)
	if !bytes.Equal(cx, c4) {
		t.Errorf("ECBytes(%x, 2) = %x\nECBytes(%x, 2) = %x\nxor = %x\nECBytes(%x, 2) = %x",
			d2, c2, d3, c3, cx, d4, c4)
	}
}

func TestGaussJordan(t *testing.T) {

	m := make([][]byte, 16)
	for i := range m {
		m[i] = make([]byte, 4)
		m[i][i/8] = 1<<uint(i%8)
		copy(m[i][2:], f.ECBytes(m[i][:2], 2))
	}
	fmt.Printf("---\n")
	for _, row := range m {
		fmt.Printf("%x\n", row)
	}
	b := []uint{0,1,2,3,12,13,14,15,20,21,22,23,24,25,26,27}
	for i := 0; i < 16; i++ {
		bi := b[i]
		if m[i][bi/8] & (1<<(7-bi%8)) == 0 {
			for j := i+1;; j++ {
				if j >= len(m) {
					t.Errorf("lost track for %d", bi)
					break
				}
				if m[j][bi/8] & (1<<(7-bi%8)) != 0 {
					m[i], m[j] = m[j], m[i]
					break
				}
			}
		}
		for j := i+1; j < len(m); j++ {
			if m[j][bi/8] & (1<<(7-bi%8)) != 0 {
				for k := range m[j] {
					m[j][k] ^= m[i][k]
				}
			}
		}
	}
	fmt.Printf("---\n")
	for _, row := range m {
		fmt.Printf("%x\n", row)
	}
	for i := 15; i >= 0; i-- {
		bi := b[i]
		for j := i-1; j >= 0; j-- {
			if m[j][bi/8] & (1<<(7-bi%8)) != 0 {
				for k := range m[j] {
					m[j][k] ^= m[i][k]
				}
			}
		}
	}
	fmt.Printf("---\n")
	for _, row := range m {
		fmt.Printf("%x", row)
		if out := f.ECBytes(row[:2], 2); !bytes.Equal(out, row[2:]) {
			fmt.Printf(" - want %x", out)
		}
		fmt.Printf("\n")
	}
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package ids

import (
	"fmt"
	"strings"
)

const (
	minUniqueBagSize = 16
)

// UniqueBag ...
type UniqueBag map[[32]byte]BitSet

func (b *UniqueBag) init() {
	if *b == nil {
		*b = make(map[[32]byte]BitSet, minUniqueBagSize)
	}
}

// Add ...
func (b *UniqueBag) Add(setID uint, idSet ...ID) {
	bs := BitSet(0)
	bs.Add(setID)

	for _, id := range idSet {
		b.UnionSet(id, bs)
	}
}

// UnionSet ...
func (b *UniqueBag) UnionSet(id ID, set BitSet) {
	b.init()

	key := id.Key()
	previousSet := (*b)[key]
	previousSet.Union(set)
	(*b)[key] = previousSet
}

// DifferenceSet ...
func (b *UniqueBag) DifferenceSet(id ID, set BitSet) {
	b.init()

	key := id.Key()
	previousSet := (*b)[key]
	previousSet.Difference(set)
	(*b)[key] = previousSet
}

// Difference ...
func (b *UniqueBag) Difference(diff *UniqueBag) {
	b.init()

	for key, previousSet := range *b {
		if previousSetDiff, exists := (*diff)[key]; exists {
			previousSet.Difference(previousSetDiff)
		}
		(*b)[key] = previousSet
	}
}

// GetSet ...
func (b *UniqueBag) GetSet(id ID) BitSet { return (*b)[*id.ID] }

// RemoveSet ...
func (b *UniqueBag) RemoveSet(id ID) { delete(*b, id.Key()) }

// List ...
func (b *UniqueBag) List() []ID {
	idList := []ID(nil)
	for id := range *b {
		idList = append(idList, NewID(id))
	}
	return idList
}

// Bag ...
func (b *UniqueBag) Bag(alpha int) Bag {
	bag := Bag{}
	bag.SetThreshold(alpha)
	for id, bs := range *b {
		bag.AddCount(NewID(id), bs.Len())
	}
	return bag
}

func (b *UniqueBag) String() string {
	sb := strings.Builder{}

	sb.WriteString(fmt.Sprintf("UniqueBag: (Size = %d)", len(*b)))
	for idBytes, set := range *b {
		id := NewID(idBytes)
		sb.WriteString(fmt.Sprintf("\n    ID[%s]: Members = %s", id, set))
	}

	return sb.String()
}
<commit_msg>pre-allocate memory<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package ids

import (
	"fmt"
	"strings"
)

const (
	minUniqueBagSize = 16
)

// UniqueBag ...
type UniqueBag map[[32]byte]BitSet

func (b *UniqueBag) init() {
	if *b == nil {
		*b = make(map[[32]byte]BitSet, minUniqueBagSize)
	}
}

// Add ...
func (b *UniqueBag) Add(setID uint, idSet ...ID) {
	bs := BitSet(0)
	bs.Add(setID)

	for _, id := range idSet {
		b.UnionSet(id, bs)
	}
}

// UnionSet ...
func (b *UniqueBag) UnionSet(id ID, set BitSet) {
	b.init()

	key := id.Key()
	previousSet := (*b)[key]
	previousSet.Union(set)
	(*b)[key] = previousSet
}

// DifferenceSet ...
func (b *UniqueBag) DifferenceSet(id ID, set BitSet) {
	b.init()

	key := id.Key()
	previousSet := (*b)[key]
	previousSet.Difference(set)
	(*b)[key] = previousSet
}

// Difference ...
func (b *UniqueBag) Difference(diff *UniqueBag) {
	b.init()

	for key, previousSet := range *b {
		if previousSetDiff, exists := (*diff)[key]; exists {
			previousSet.Difference(previousSetDiff)
		}
		(*b)[key] = previousSet
	}
}

// GetSet ...
func (b *UniqueBag) GetSet(id ID) BitSet { return (*b)[*id.ID] }

// RemoveSet ...
func (b *UniqueBag) RemoveSet(id ID) { delete(*b, id.Key()) }

// List ...
func (b *UniqueBag) List() []ID {
	idList := make([]ID, len(*b), len(*b))
	i := 0
	for id := range *b {
		idList[i] = NewID(id)
		i++
	}
	return idList
}

// Bag ...
func (b *UniqueBag) Bag(alpha int) Bag {
	bag := Bag{}
	bag.SetThreshold(alpha)
	for id, bs := range *b {
		bag.AddCount(NewID(id), bs.Len())
	}
	return bag
}

func (b *UniqueBag) String() string {
	sb := strings.Builder{}

	sb.WriteString(fmt.Sprintf("UniqueBag: (Size = %d)", len(*b)))
	for idBytes, set := range *b {
		id := NewID(idBytes)
		sb.WriteString(fmt.Sprintf("\n    ID[%s]: Members = %s", id, set))
	}

	return sb.String()
}
<|endoftext|>