<commit_before>package temperature

import (
	"math"

	"github.com/ready-steady/hotspot"
	"github.com/ready-steady/linal/decomp"
	"github.com/ready-steady/linal/matrix"
)

// Solver represents the algorithm for temperature analysis configured for a
// particular problem.
type Solver struct {
	Config Config

	Cores uint32
	Nodes uint32

	system system
}

// New returns an instance of the algorithm set up according to the given
// configuration.
func New(c Config) (*Solver, error) {
	s := &Solver{
		Config: c,
	}

	model := hotspot.New(c.Floorplan, c.HotSpot.Config, c.HotSpot.Params)

	cc := model.Cores
	nc := model.Nodes

	var i, j uint32

	// Reusing model.G to store A and model.C to store D.
	A := model.G
	D := model.C
	for i = 0; i < nc; i++ {
		D[i] = math.Sqrt(1 / model.C[i])
	}
	for i = 0; i < nc; i++ {
		for j = 0; j < nc; j++ {
			A[j*nc+i] = -1 * D[i] * D[j] * A[j*nc+i]
		}
	}

	// Reusing A (which is model.G) to store U.
	U := A
	Λ := make([]float64, nc)
	if err := decomp.SymEig(A, U, Λ, nc); err != nil {
		return nil, err
	}

	Δt := c.TimeStep

	coef := make([]float64, nc)
	temp := make([]float64, nc*nc)

	for i = 0; i < nc; i++ {
		coef[i] = math.Exp(Δt * Λ[i])
	}
	for i = 0; i < nc; i++ {
		for j = 0; j < nc; j++ {
			temp[j*nc+i] = coef[i] * U[i*nc+j]
		}
	}

	E := make([]float64, nc*nc)
	matrix.Multiply(U, temp, E, nc, nc, nc)

	// Technically, temp = temp[0 : nc*cc].
	for i = 0; i < nc; i++ {
		coef[i] = (coef[i] - 1) / Λ[i]
	}
	for i = 0; i < nc; i++ {
		for j = 0; j < cc; j++ {
			temp[j*nc+i] = coef[i] * U[i*nc+j] * D[j]
		}
	}

	F := make([]float64, nc*cc)
	matrix.Multiply(U, temp, F, nc, nc, cc)

	s.Cores = model.Cores
	s.Nodes = model.Nodes

	s.system.D = D

	s.system.Λ = Λ
	s.system.U = U

	s.system.E = E
	s.system.F = F

	return s, nil
}

// Load returns an instance of the algorithm set up according to the given
// configuration file.
func Load(path string) (*Solver, error) {
	config, err := loadConfig(path)
	if err != nil {
		return nil, err
	}

	return New(config)
}
<commit_msg>Updated the usage of linal/decomp<commit_after>package temperature

import (
	"math"

	"github.com/ready-steady/hotspot"
	"github.com/ready-steady/linal/decomposition"
	"github.com/ready-steady/linal/matrix"
)

// Solver represents the algorithm for temperature analysis configured for a
// particular problem.
type Solver struct {
	Config Config

	Cores uint32
	Nodes uint32

	system system
}

// New returns an instance of the algorithm set up according to the given
// configuration.
func New(c Config) (*Solver, error) {
	s := &Solver{
		Config: c,
	}

	model := hotspot.New(c.Floorplan, c.HotSpot.Config, c.HotSpot.Params)

	cc := model.Cores
	nc := model.Nodes

	var i, j uint32

	// Reusing model.G to store A and model.C to store D.
	A := model.G
	D := model.C
	for i = 0; i < nc; i++ {
		D[i] = math.Sqrt(1 / model.C[i])
	}
	for i = 0; i < nc; i++ {
		for j = 0; j < nc; j++ {
			A[j*nc+i] = -1 * D[i] * D[j] * A[j*nc+i]
		}
	}

	// Reusing A (which is model.G) to store U.
	U := A
	Λ := make([]float64, nc)
	if err := decomposition.SymEig(A, U, Λ, nc); err != nil {
		return nil, err
	}

	Δt := c.TimeStep

	coef := make([]float64, nc)
	temp := make([]float64, nc*nc)

	for i = 0; i < nc; i++ {
		coef[i] = math.Exp(Δt * Λ[i])
	}
	for i = 0; i < nc; i++ {
		for j = 0; j < nc; j++ {
			temp[j*nc+i] = coef[i] * U[i*nc+j]
		}
	}

	E := make([]float64, nc*nc)
	matrix.Multiply(U, temp, E, nc, nc, nc)

	// Technically, temp = temp[0 : nc*cc].
	for i = 0; i < nc; i++ {
		coef[i] = (coef[i] - 1) / Λ[i]
	}
	for i = 0; i < nc; i++ {
		for j = 0; j < cc; j++ {
			temp[j*nc+i] = coef[i] * U[i*nc+j] * D[j]
		}
	}

	F := make([]float64, nc*cc)
	matrix.Multiply(U, temp, F, nc, nc, cc)

	s.Cores = model.Cores
	s.Nodes = model.Nodes

	s.system.D = D

	s.system.Λ = Λ
	s.system.U = U

	s.system.E = E
	s.system.F = F

	return s, nil
}

// Load returns an instance of the algorithm set up according to the given
// configuration file.
func Load(path string) (*Solver, error) {
	config, err := loadConfig(path)
	if err != nil {
		return nil, err
	}

	return New(config)
}
<|endoftext|>


<commit_before>/*
   DNS-over-HTTPS
   Copyright (C) 2017-2018 Star Brilliant <m13253@hotmail.com>

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/

package main

import (
	"fmt"
	"log"
	"math/rand"
	"net"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/gorilla/handlers"
	"github.com/m13253/dns-over-https/json-dns"
	"github.com/miekg/dns"
)

type Server struct {
	conf      *config
	udpClient *dns.Client
	tcpClient *dns.Client
	servemux  *http.ServeMux
}

type DNSRequest struct {
	request    *dns.Msg
	response   *dns.Msg
	isTailored bool
	errcode    int
	errtext    string
}

func NewServer(conf *config) (s *Server) {
	s = &Server{
		conf: conf,
		udpClient: &dns.Client{
			Net:     "udp",
			Timeout: time.Duration(conf.Timeout) * time.Second,
		},
		tcpClient: &dns.Client{
			Net:     "tcp",
			Timeout: time.Duration(conf.Timeout) * time.Second,
		},
		servemux: http.NewServeMux(),
	}
	s.servemux.HandleFunc(conf.Path, s.handlerFunc)
	return
}

func (s *Server) Start() error {
	servemux := http.Handler(s.servemux)
	if s.conf.Verbose {
		servemux = handlers.CombinedLoggingHandler(os.Stdout, servemux)
	}
	results := make(chan error, len(s.conf.Listen))
	for _, addr := range s.conf.Listen {
		go func(addr string) {
			var err error
			if s.conf.Cert != "" || s.conf.Key != "" {
				err = http.ListenAndServeTLS(addr, s.conf.Cert, s.conf.Key, servemux)
			} else {
				err = http.ListenAndServe(addr, servemux)
			}
			if err != nil {
				log.Println(err)
			}
			results <- err
		}(addr)
	}
	// wait for all handlers
	for i := 0; i < cap(results); i++ {
		err := <-results
		if err != nil {
			return err
		}
	}
	close(results)
	return nil
}

func (s *Server) handlerFunc(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Server", USER_AGENT)
	w.Header().Set("X-Powered-By", USER_AGENT)

	if r.Form == nil {
		const maxMemory = 32 << 20 // 32 MB
		r.ParseMultipartForm(maxMemory)
	}
	contentType := r.Header.Get("Content-Type")
	if ct := r.FormValue("ct"); ct != "" {
		contentType = ct
	}
	if contentType == "" {
		// Guess request Content-Type based on other parameters
		if r.FormValue("name") != "" {
			contentType = "application/dns-json"
		} else if r.FormValue("dns") != "" {
			contentType = "application/dns-message"
		}
	}
	var responseType string
	for _, responseCandidate := range strings.Split(r.Header.Get("Accept"), ",") {
		responseCandidate = strings.SplitN(responseCandidate, ";", 2)[0]
		if responseCandidate == "application/json" {
			responseType = "application/json"
			break
		} else if responseCandidate == "application/dns-udpwireformat" {
			responseType = "application/dns-message"
			break
		} else if responseCandidate == "application/dns-message" {
			responseType = "application/dns-message"
			break
		}
	}
	if responseType == "" {
		// Guess response Content-Type based on request Content-Type
		if contentType == "application/dns-json" {
			responseType = "application/json"
		} else if contentType == "application/dns-message" {
			responseType = "application/dns-message"
		} else if contentType == "application/dns-udpwireformat" {
			responseType = "application/dns-message"
		}
	}

	var req *DNSRequest
	if contentType == "application/dns-json" {
		req = s.parseRequestGoogle(w, r)
	} else if contentType == "application/dns-message" {
		req = s.parseRequestIETF(w, r)
	} else if contentType == "application/dns-udpwireformat" {
		req = s.parseRequestIETF(w, r)
	} else {
		jsonDNS.FormatError(w, fmt.Sprintf("Invalid argument value: \"ct\" = %q", contentType), 415)
		return
	}
	if req.errcode != 0 {
		jsonDNS.FormatError(w, req.errtext, req.errcode)
		return
	}

	var err error
	req.response, err = s.doDNSQuery(req.request)
	if err != nil {
		jsonDNS.FormatError(w, fmt.Sprintf("DNS query failure (%s)", err.Error()), 503)
		return
	}

	if responseType == "application/json" {
		s.generateResponseGoogle(w, r, req)
	} else if responseType == "application/dns-message" {
		s.generateResponseIETF(w, r, req)
	} else {
		panic("Unknown response Content-Type")
	}
}

func (s *Server) findClientIP(r *http.Request) net.IP {
	XForwardedFor := r.Header.Get("X-Forwarded-For")
	if XForwardedFor != "" {
		for _, addr := range strings.Split(XForwardedFor, ",") {
			addr = strings.TrimSpace(addr)
			ip := net.ParseIP(addr)
			if jsonDNS.IsGlobalIP(ip) {
				return ip
			}
		}
	}
	XRealIP := r.Header.Get("X-Real-IP")
	if XRealIP != "" {
		addr := strings.TrimSpace(XRealIP)
		ip := net.ParseIP(addr)
		if jsonDNS.IsGlobalIP(ip) {
			return ip
		}
	}
	remoteAddr, err := net.ResolveTCPAddr("tcp", r.RemoteAddr)
	if err != nil {
		return nil
	}
	if ip := remoteAddr.IP; jsonDNS.IsGlobalIP(ip) {
		return ip
	}
	return nil
}

func (s *Server) doDNSQuery(msg *dns.Msg) (resp *dns.Msg, err error) {
	numServers := len(s.conf.Upstream)
	for i := uint(0); i < s.conf.Tries; i++ {
		server := s.conf.Upstream[rand.Intn(numServers)]
		if !s.conf.TCPOnly {
			resp, _, err = s.udpClient.Exchange(msg, server)
			if err == dns.ErrTruncated {
				log.Println(err)
				resp, _, err = s.tcpClient.Exchange(msg, server)
			}
		} else {
			resp, _, err = s.tcpClient.Exchange(msg, server)
		}
		if err == nil {
			return
		}
		log.Println(err)
	}
	return
}
<commit_msg>Use dns.DefaultMsgSize instead of magic number 4096<commit_after>/*
   DNS-over-HTTPS
   Copyright (C) 2017-2018 Star Brilliant <m13253@hotmail.com>

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/

package main

import (
	"fmt"
	"log"
	"math/rand"
	"net"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/gorilla/handlers"
	"github.com/m13253/dns-over-https/json-dns"
	"github.com/miekg/dns"
)

type Server struct {
	conf      *config
	udpClient *dns.Client
	tcpClient *dns.Client
	servemux  *http.ServeMux
}

type DNSRequest struct {
	request    *dns.Msg
	response   *dns.Msg
	isTailored bool
	errcode    int
	errtext    string
}

func NewServer(conf *config) (s *Server) {
	s = &Server{
		conf: conf,
		udpClient: &dns.Client{
			Net:     "udp",
			UDPSize: 4096,
			Timeout: time.Duration(conf.Timeout) * time.Second,
		},
		tcpClient: &dns.Client{
			Net:     "tcp",
			Timeout: time.Duration(conf.Timeout) * time.Second,
		},
		servemux: http.NewServeMux(),
	}
	s.servemux.HandleFunc(conf.Path, s.handlerFunc)
	return
}

func (s *Server) Start() error {
	servemux := http.Handler(s.servemux)
	if s.conf.Verbose {
		servemux = handlers.CombinedLoggingHandler(os.Stdout, servemux)
	}
	results := make(chan error, len(s.conf.Listen))
	for _, addr := range s.conf.Listen {
		go func(addr string) {
			var err error
			if s.conf.Cert != "" || s.conf.Key != "" {
				err = http.ListenAndServeTLS(addr, s.conf.Cert, s.conf.Key, servemux)
			} else {
				err = http.ListenAndServe(addr, servemux)
			}
			if err != nil {
				log.Println(err)
			}
			results <- err
		}(addr)
	}
	// wait for all handlers
	for i := 0; i < cap(results); i++ {
		err := <-results
		if err != nil {
			return err
		}
	}
	close(results)
	return nil
}

func (s *Server) handlerFunc(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Server", USER_AGENT)
	w.Header().Set("X-Powered-By", USER_AGENT)

	if r.Form == nil {
		const maxMemory = 32 << 20 // 32 MB
		r.ParseMultipartForm(maxMemory)
	}
	contentType := r.Header.Get("Content-Type")
	if ct := r.FormValue("ct"); ct != "" {
		contentType = ct
	}
	if contentType == "" {
		// Guess request Content-Type based on other parameters
		if r.FormValue("name") != "" {
			contentType = "application/dns-json"
		} else if r.FormValue("dns") != "" {
			contentType = "application/dns-message"
		}
	}
	var responseType string
	for _, responseCandidate := range strings.Split(r.Header.Get("Accept"), ",") {
		responseCandidate = strings.SplitN(responseCandidate, ";", 2)[0]
		if responseCandidate == "application/json" {
			responseType = "application/json"
			break
		} else if responseCandidate == "application/dns-udpwireformat" {
			responseType = "application/dns-message"
			break
		} else if responseCandidate == "application/dns-message" {
			responseType = "application/dns-message"
			break
		}
	}
	if responseType == "" {
		// Guess response Content-Type based on request Content-Type
		if contentType == "application/dns-json" {
			responseType = "application/json"
		} else if contentType == "application/dns-message" {
			responseType = "application/dns-message"
		} else if contentType == "application/dns-udpwireformat" {
			responseType = "application/dns-message"
		}
	}

	var req *DNSRequest
	if contentType == "application/dns-json" {
		req = s.parseRequestGoogle(w, r)
	} else if contentType == "application/dns-message" {
		req = s.parseRequestIETF(w, r)
	} else if contentType == "application/dns-udpwireformat" {
		req = s.parseRequestIETF(w, r)
	} else {
		jsonDNS.FormatError(w, fmt.Sprintf("Invalid argument value: \"ct\" = %q", contentType), 415)
		return
	}
	if req.errcode != 0 {
		jsonDNS.FormatError(w, req.errtext, req.errcode)
		return
	}

	var err error
	req.response, err = s.doDNSQuery(req.request)
	if err != nil {
		jsonDNS.FormatError(w, fmt.Sprintf("DNS query failure (%s)", err.Error()), 503)
		return
	}

	if responseType == "application/json" {
		s.generateResponseGoogle(w, r, req)
	} else if responseType == "application/dns-message" {
		s.generateResponseIETF(w, r, req)
	} else {
		panic("Unknown response Content-Type")
	}
}

func (s *Server) findClientIP(r *http.Request) net.IP {
	XForwardedFor := r.Header.Get("X-Forwarded-For")
	if XForwardedFor != "" {
		for _, addr := range strings.Split(XForwardedFor, ",") {
			addr = strings.TrimSpace(addr)
			ip := net.ParseIP(addr)
			if jsonDNS.IsGlobalIP(ip) {
				return ip
			}
		}
	}
	XRealIP := r.Header.Get("X-Real-IP")
	if XRealIP != "" {
		addr := strings.TrimSpace(XRealIP)
		ip := net.ParseIP(addr)
		if jsonDNS.IsGlobalIP(ip) {
			return ip
		}
	}
	remoteAddr, err := net.ResolveTCPAddr("tcp", r.RemoteAddr)
	if err != nil {
		return nil
	}
	if ip := remoteAddr.IP; jsonDNS.IsGlobalIP(ip) {
		return ip
	}
	return nil
}

func (s *Server) doDNSQuery(msg *dns.Msg) (resp *dns.Msg, err error) {
	numServers := len(s.conf.Upstream)
	for i := uint(0); i < s.conf.Tries; i++ {
		server := s.conf.Upstream[rand.Intn(numServers)]
		if !s.conf.TCPOnly {
			resp, _, err = s.udpClient.Exchange(msg, server)
			if err == dns.ErrTruncated {
				log.Println(err)
				resp, _, err = s.tcpClient.Exchange(msg, server)
			}
		} else {
			resp, _, err = s.tcpClient.Exchange(msg, server)
		}
		if err == nil {
			return
		}
		log.Println(err)
	}
	return
}
<|endoftext|>


<commit_before>package match

import (
	"fmt"

	"github.com/tisp-lang/tisp/src/lib/ast"
	"github.com/tisp-lang/tisp/src/lib/debug"
	"github.com/tisp-lang/tisp/src/lib/gensym"
	"github.com/tisp-lang/tisp/src/lib/scalar"
)

type desugarer struct {
	letBoundNames, lets []interface{}
}

func newDesugarer() *desugarer {
	return &desugarer{nil, nil}
}

func (d *desugarer) desugar(x interface{}) interface{} {
	switch x := x.(type) {
	case ast.App:
		return ast.NewApp(
			d.desugar(x.Function()),
			d.desugar(x.Arguments()).(ast.Arguments),
			x.DebugInfo())
	case ast.Arguments:
		ps := make([]ast.PositionalArgument, 0, len(x.Positionals()))

		for _, p := range x.Positionals() {
			ps = append(ps, d.desugar(p).(ast.PositionalArgument))
		}

		ks := make([]ast.KeywordArgument, 0, len(x.Keywords()))

		for _, k := range x.Keywords() {
			ks = append(ks, d.desugar(k).(ast.KeywordArgument))
		}

		dicts := make([]interface{}, 0, len(x.ExpandedDicts()))

		for _, dict := range x.ExpandedDicts() {
			dicts = append(dicts, d.desugar(dict))
		}

		return ast.NewArguments(ps, ks, dicts)
	case ast.KeywordArgument:
		return ast.NewKeywordArgument(x.Name(), d.desugar(x.Value()))
	case ast.LetFunction:
		ls := make([]interface{}, 0, len(x.Lets()))

		for _, l := range x.Lets() {
			l := d.desugar(l)
			ls = append(ls, append(d.takeLets(), l)...)
		}

		b := d.desugar(x.Body())
		ls = append(ls, d.takeLets()...)

		return ast.NewLetFunction(
			x.Name(),
			x.Signature(),
			ls,
			b,
			x.DebugInfo())
	case ast.LetVar:
		return ast.NewLetVar(x.Name(), d.desugar(x.Expr()))
	case ast.Match:
		cs := make([]ast.MatchCase, 0, len(x.Cases()))

		for _, c := range x.Cases() {
			cs = append(cs, renameBoundNamesInCase(ast.NewMatchCase(c.Pattern(), d.desugar(c.Value()))))
		}

		return d.resultApp(d.createMatchFunction(cs), d.desugar(x.Value()))
	case ast.Output:
		return ast.NewOutput(d.desugar(x.Expr()), x.Expanded())
	case ast.PositionalArgument:
		return ast.NewPositionalArgument(d.desugar(x.Value()), x.Expanded())
	default:
		return x
	}
}

func (d *desugarer) takeLets() []interface{} {
	ls := append(d.letBoundNames, d.lets...)
	d.letBoundNames = nil
	d.lets = nil
	return ls
}

func (d *desugarer) letTempVar(v interface{}) string {
	s := gensym.GenSym("match", "tmp")
	d.lets = append(d.lets, ast.NewLetVar(s, v))
	return s
}

func (d *desugarer) bindName(s string, v interface{}) string {
	d.letBoundNames = append(d.letBoundNames, ast.NewLetVar(s, v))
	return s
}

// matchedApp applies a function to arguments and creates a matched value of
// match expression.
func (d *desugarer) matchedApp(f interface{}, args ...interface{}) string {
	return d.bindName(gensym.GenSym("match", "app"), app(f, args...))
}

// resultApp applies a function to arguments and creates a result value of match
// expression.
func (d *desugarer) resultApp(f interface{}, args ...interface{}) string {
	return d.letTempVar(app(f, args...))
}

func (d *desugarer) createMatchFunction(cs []ast.MatchCase) interface{} {
	arg := gensym.GenSym("match", "argument")
	body := d.desugarCases(arg, cs, "$matchError")

	f := ast.NewLetFunction(
		gensym.GenSym("match", "function"),
		ast.NewSignature([]string{arg}, nil, "", nil, nil, ""),
		d.takeLets(),
		body,
		debug.NewGoInfo(0))

	d.lets = append(d.lets, f)

	return f.Name()
}

func (d *desugarer) desugarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	css := groupCases(cs)

	if cs, ok := css[namePattern]; ok {
		c := cs[0]
		d.bindName(c.Pattern().(string), v)
		dc = c.Value()
	}

	ks := []ast.SwitchCase{}

	if cs, ok := css[listPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"list\"", d.desugarListCases(v, cs, dc)))
	}

	if cs, ok := css[dictPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"dict\"", d.desugarDictCases(v, cs, dc)))
	}

	if cs, ok := css[scalarPattern]; ok {
		dc = d.desugarScalarCases(v, cs, dc)
	}

	return newSwitch(d.resultApp("$typeOf", v), ks, dc)
}

func groupCases(cs []ast.MatchCase) map[patternType][]ast.MatchCase {
	css := map[patternType][]ast.MatchCase{}

	for i, c := range cs {
		t := getPatternType(c.Pattern())

		if t == namePattern && i < len(cs)-1 {
			panic("A wildcard pattern is found while some patterns are left")
		}

		css[t] = append(css[t], c)
	}

	return css
}

func getPatternType(p interface{}) patternType {
	switch x := p.(type) {
	case string:
		if scalar.Defined(x) {
			return scalarPattern
		}

		return namePattern
	case ast.App:
		switch x.Function().(string) {
		case "$list":
			return listPattern
		case "$dict":
			return dictPattern
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}

func (d *desugarer) desugarListCases(list interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		first interface{}
		cases []ast.MatchCase
	}

	gs := []group{}
	first := d.matchedApp("$first", list)
	rest := d.matchedApp("$rest", list)

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			dc = d.resultApp("$if", app("$=", list, "$emptyList"), c.Value(), dc)
			continue
		}

		if ps[0].Expanded() {
			panic("Not implemented")
		}

		v := ps[0].Value()

		c = ast.NewMatchCase(
			ast.NewApp("$list", ast.NewArguments(ps[1:], nil, nil), debug.NewGoInfo(0)),
			c.Value())

		if getPatternType(v) == namePattern {
			d.bindName(v.(string), first)
			dc = d.desugarCases(
				rest,
				[]ast.MatchCase{c},
				d.desugarListCases(list, cs[i+1:], dc))
			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(v, g.first) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{v, []ast.MatchCase{c}})
		}
	}

	ks := make([]ast.MatchCase, 0, len(gs))
	dc = d.letTempVar(dc)

	for _, g := range gs {
		ks = append(ks, ast.NewMatchCase(g.first, d.desugarCases(rest, g.cases, dc)))
	}

	return d.desugarCases(first, ks, dc)
}

func (d *desugarer) desugarDictCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		key   interface{}
		cases []ast.MatchCase
	}

	gs := []group{}

	for _, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			dc = d.resultApp("$if", app("$=", v, "$emptyDict"), c.Value(), dc)
			continue
		}

		if ps[0].Expanded() {
			panic("Not implemented")
		}

		g := group{ps[0].Value(), []ast.MatchCase{c}}

		if len(gs) == 0 {
			gs = append(gs, g)
		} else if last := gs[len(gs)-1]; equalPatterns(g.key, last.key) {
			last.cases = append(last.cases, c)
		} else {
			gs = append(gs, g)
		}
	}

	for i := len(gs) - 1; i >= 0; i-- {
		g := gs[i]
		dc = d.resultApp("$if",
			app("$include", v, g.key),
			d.desugarDictCasesOfSameKey(v, g.cases, dc),
			dc)
	}

	return dc
}

func (d *desugarer) desugarDictCasesOfSameKey(dict interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		value interface{}
		cases []ast.MatchCase
	}

	key := cs[0].Pattern().(ast.App).Arguments().Positionals()[0].Value()
	value := d.matchedApp(dict, key)
	newDict := d.matchedApp("delete", dict, key)
	gs := []group{}

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()
		v := ps[1].Value()

		c = ast.NewMatchCase(
			ast.NewApp("$dict", ast.NewArguments(ps[2:], nil, nil), debug.NewGoInfo(0)),
			c.Value())

		if getPatternType(v) == namePattern {
			d.bindName(v.(string), value)

			if rest := cs[i+1:]; len(rest) != 0 {
				dc = d.desugarDictCasesOfSameKey(dict, rest, dc)
			}

			dc = d.desugarCases(newDict, []ast.MatchCase{c}, dc)

			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(v, g.value) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{v, []ast.MatchCase{c}})
		}
	}

	cs = make([]ast.MatchCase, 0, len(gs))
	dc = d.letTempVar(dc)

	for _, g := range gs {
		cs = append(
			cs,
			ast.NewMatchCase(g.value, d.desugarCases(newDict, g.cases, dc)))
	}

	return d.desugarCases(value, cs, dc)
}

func (d *desugarer) desugarScalarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	ks := []ast.SwitchCase{}

	for _, c := range cs {
		ks = append(ks, ast.NewSwitchCase(c.Pattern().(string), c.Value()))
	}

	return newSwitch(v, ks, dc)
}

func renameBoundNamesInCase(c ast.MatchCase) ast.MatchCase {
	p, ns := newPatternRenamer().rename(c.Pattern())
	return ast.NewMatchCase(p, newValueRenamer(ns).rename(c.Value()))
}

func equalPatterns(p, q interface{}) bool {
	switch x := p.(type) {
	case string:
		y, ok := q.(string)

		if !ok {
			return false
		}

		return x == y
	case ast.App:
		y, ok := q.(ast.App)

		if !ok ||
			x.Function().(string) != y.Function().(string) ||
			len(x.Arguments().Positionals()) != len(y.Arguments().Positionals()) {
			return false
		}

		for i := range x.Arguments().Positionals() {
			p := x.Arguments().Positionals()[i]
			q := y.Arguments().Positionals()[i]

			if p.Expanded() != q.Expanded() || !equalPatterns(p.Value(), q.Value()) {
				return false
			}
		}

		return true
	}

	panic(fmt.Errorf("Invalid pattern: %#v, %#v", p, q))
}
<commit_msg>Desugar match expressions in mutual recursions<commit_after>package match

import (
	"fmt"

	"github.com/tisp-lang/tisp/src/lib/ast"
	"github.com/tisp-lang/tisp/src/lib/debug"
	"github.com/tisp-lang/tisp/src/lib/gensym"
	"github.com/tisp-lang/tisp/src/lib/scalar"
)

type desugarer struct {
	letBoundNames, lets []interface{}
}

func newDesugarer() *desugarer {
	return &desugarer{nil, nil}
}

func (d *desugarer) desugar(x interface{}) interface{} {
	switch x := x.(type) {
	case ast.App:
		return ast.NewApp(
			d.desugar(x.Function()),
			d.desugar(x.Arguments()).(ast.Arguments),
			x.DebugInfo())
	case ast.Arguments:
		ps := make([]ast.PositionalArgument, 0, len(x.Positionals()))

		for _, p := range x.Positionals() {
			ps = append(ps, d.desugar(p).(ast.PositionalArgument))
		}

		ks := make([]ast.KeywordArgument, 0, len(x.Keywords()))

		for _, k := range x.Keywords() {
			ks = append(ks, d.desugar(k).(ast.KeywordArgument))
		}

		dicts := make([]interface{}, 0, len(x.ExpandedDicts()))

		for _, dict := range x.ExpandedDicts() {
			dicts = append(dicts, d.desugar(dict))
		}

		return ast.NewArguments(ps, ks, dicts)
	case ast.KeywordArgument:
		return ast.NewKeywordArgument(x.Name(), d.desugar(x.Value()))
	case ast.LetFunction:
		ls := make([]interface{}, 0, len(x.Lets()))

		for _, l := range x.Lets() {
			l := d.desugar(l)
			ls = append(ls, append(d.takeLets(), l)...)
		}

		b := d.desugar(x.Body())
		ls = append(ls, d.takeLets()...)

		return ast.NewLetFunction(
			x.Name(),
			x.Signature(),
			ls,
			b,
			x.DebugInfo())
	case ast.LetVar:
		return ast.NewLetVar(x.Name(), d.desugar(x.Expr()))
	case ast.Match:
		cs := make([]ast.MatchCase, 0, len(x.Cases()))

		for _, c := range x.Cases() {
			cs = append(cs, renameBoundNamesInCase(ast.NewMatchCase(c.Pattern(), d.desugar(c.Value()))))
		}

		return d.resultApp(d.createMatchFunction(cs), d.desugar(x.Value()))
	case ast.MutualRecursion:
		fs := make([]ast.LetFunction, 0, len(x.LetFunctions()))

		for _, f := range x.LetFunctions() {
			fs = append(fs, d.desugar(f).(ast.LetFunction))
		}

		return ast.NewMutualRecursion(fs, x.DebugInfo())
	case ast.Output:
		return ast.NewOutput(d.desugar(x.Expr()), x.Expanded())
	case ast.PositionalArgument:
		return ast.NewPositionalArgument(d.desugar(x.Value()), x.Expanded())
	default:
		return x
	}
}

func (d *desugarer) takeLets() []interface{} {
	ls := append(d.letBoundNames, d.lets...)
	d.letBoundNames = nil
	d.lets = nil
	return ls
}

func (d *desugarer) letTempVar(v interface{}) string {
	s := gensym.GenSym("match", "tmp")
	d.lets = append(d.lets, ast.NewLetVar(s, v))
	return s
}

func (d *desugarer) bindName(s string, v interface{}) string {
	d.letBoundNames = append(d.letBoundNames, ast.NewLetVar(s, v))
	return s
}

// matchedApp applies a function to arguments and creates a matched value of
// match expression.
func (d *desugarer) matchedApp(f interface{}, args ...interface{}) string {
	return d.bindName(gensym.GenSym("match", "app"), app(f, args...))
}

// resultApp applies a function to arguments and creates a result value of match
// expression.
func (d *desugarer) resultApp(f interface{}, args ...interface{}) string {
	return d.letTempVar(app(f, args...))
}

func (d *desugarer) createMatchFunction(cs []ast.MatchCase) interface{} {
	arg := gensym.GenSym("match", "argument")
	body := d.desugarCases(arg, cs, "$matchError")

	f := ast.NewLetFunction(
		gensym.GenSym("match", "function"),
		ast.NewSignature([]string{arg}, nil, "", nil, nil, ""),
		d.takeLets(),
		body,
		debug.NewGoInfo(0))

	d.lets = append(d.lets, f)

	return f.Name()
}

func (d *desugarer) desugarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	css := groupCases(cs)

	if cs, ok := css[namePattern]; ok {
		c := cs[0]
		d.bindName(c.Pattern().(string), v)
		dc = c.Value()
	}

	ks := []ast.SwitchCase{}

	if cs, ok := css[listPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"list\"", d.desugarListCases(v, cs, dc)))
	}

	if cs, ok := css[dictPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"dict\"", d.desugarDictCases(v, cs, dc)))
	}

	if cs, ok := css[scalarPattern]; ok {
		dc = d.desugarScalarCases(v, cs, dc)
	}

	return newSwitch(d.resultApp("$typeOf", v), ks, dc)
}

func groupCases(cs []ast.MatchCase) map[patternType][]ast.MatchCase {
	css := map[patternType][]ast.MatchCase{}

	for i, c := range cs {
		t := getPatternType(c.Pattern())

		if t == namePattern && i < len(cs)-1 {
			panic("A wildcard pattern is found while some patterns are left")
		}

		css[t] = append(css[t], c)
	}

	return css
}

func getPatternType(p interface{}) patternType {
	switch x := p.(type) {
	case string:
		if scalar.Defined(x) {
			return scalarPattern
		}

		return namePattern
	case ast.App:
		switch x.Function().(string) {
		case "$list":
			return listPattern
		case "$dict":
			return dictPattern
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}

func (d *desugarer) desugarListCases(list interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		first interface{}
		cases []ast.MatchCase
	}

	gs := []group{}
	first := d.matchedApp("$first", list)
	rest := d.matchedApp("$rest", list)

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			dc = d.resultApp("$if", app("$=", list, "$emptyList"), c.Value(), dc)
			continue
		}

		if ps[0].Expanded() {
			panic("Not implemented")
		}

		v := ps[0].Value()

		c = ast.NewMatchCase(
			ast.NewApp("$list", ast.NewArguments(ps[1:], nil, nil), debug.NewGoInfo(0)),
			c.Value())

		if getPatternType(v) == namePattern {
			d.bindName(v.(string), first)
			dc = d.desugarCases(
				rest,
				[]ast.MatchCase{c},
				d.desugarListCases(list, cs[i+1:], dc))
			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(v, g.first) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{v, []ast.MatchCase{c}})
		}
	}

	ks := make([]ast.MatchCase, 0, len(gs))
	dc = d.letTempVar(dc)

	for _, g := range gs {
		ks = append(ks, ast.NewMatchCase(g.first, d.desugarCases(rest, g.cases, dc)))
	}

	return d.desugarCases(first, ks, dc)
}

func (d *desugarer) desugarDictCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		key   interface{}
		cases []ast.MatchCase
	}

	gs := []group{}

	for _, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			dc = d.resultApp("$if", app("$=", v, "$emptyDict"), c.Value(), dc)
			continue
		}

		if ps[0].Expanded() {
			panic("Not implemented")
		}

		g := group{ps[0].Value(), []ast.MatchCase{c}}

		if len(gs) == 0 {
			gs = append(gs, g)
		} else if last := gs[len(gs)-1]; equalPatterns(g.key, last.key) {
			last.cases = append(last.cases, c)
		} else {
			gs = append(gs, g)
		}
	}

	for i := len(gs) - 1; i >= 0; i-- {
		g := gs[i]
		dc = d.resultApp("$if",
			app("$include", v, g.key),
			d.desugarDictCasesOfSameKey(v, g.cases, dc),
			dc)
	}

	return dc
}

func (d *desugarer) desugarDictCasesOfSameKey(dict interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		value interface{}
		cases []ast.MatchCase
	}

	key := cs[0].Pattern().(ast.App).Arguments().Positionals()[0].Value()
	value := d.matchedApp(dict, key)
	newDict := d.matchedApp("delete", dict, key)
	gs := []group{}

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()
		v := ps[1].Value()

		c = ast.NewMatchCase(
			ast.NewApp("$dict", ast.NewArguments(ps[2:], nil, nil), debug.NewGoInfo(0)),
			c.Value())

		if getPatternType(v) == namePattern {
			d.bindName(v.(string), value)

			if rest := cs[i+1:]; len(rest) != 0 {
				dc = d.desugarDictCasesOfSameKey(dict, rest, dc)
			}

			dc = d.desugarCases(newDict, []ast.MatchCase{c}, dc)

			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(v, g.value) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{v, []ast.MatchCase{c}})
		}
	}

	cs = make([]ast.MatchCase, 0, len(gs))
	dc = d.letTempVar(dc)

	for _, g := range gs {
		cs = append(
			cs,
			ast.NewMatchCase(g.value, d.desugarCases(newDict, g.cases, dc)))
	}

	return d.desugarCases(value, cs, dc)
}

func (d *desugarer) desugarScalarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	ks := []ast.SwitchCase{}

	for _, c := range cs {
		ks = append(ks, ast.NewSwitchCase(c.Pattern().(string), c.Value()))
	}

	return newSwitch(v, ks, dc)
}

func renameBoundNamesInCase(c ast.MatchCase) ast.MatchCase {
	p, ns := newPatternRenamer().rename(c.Pattern())
	return ast.NewMatchCase(p, newValueRenamer(ns).rename(c.Value()))
}

func equalPatterns(p, q interface{}) bool {
	switch x := p.(type) {
	case string:
		y, ok := q.(string)

		if !ok {
			return false
		}

		return x == y
	case ast.App:
		y, ok := q.(ast.App)

		if !ok ||
			x.Function().(string) != y.Function().(string) ||
			len(x.Arguments().Positionals()) != len(y.Arguments().Positionals()) {
			return false
		}

		for i := range x.Arguments().Positionals() {
			p := x.Arguments().Positionals()[i]
			q := y.Arguments().Positionals()[i]

			if p.Expanded() != q.Expanded() || !equalPatterns(p.Value(), q.Value()) {
				return false
			}
		}

		return true
	}

	panic(fmt.Errorf("Invalid pattern: %#v, %#v", p, q))
}
<|endoftext|>


<commit_before>/* https://leetcode.com/problems/remove-duplicates-from-sorted-array/#/description
Given a sorted array, remove the duplicates in place such that each element appear only once and return the new length.

Do not allocate extra space for another array, you must do this in place with constant memory.

For example,
Given input array nums = [1,1,2],

Your function should return length = 2, with the first two elements of nums being 1 and 2 respectively. It doesn't matter what you leave beyond the new length.
*/
package leetcode

func removeDuplicates(nums []int) int {
	n, end := len(nums), 0
	if n < 2 {
		return n
	}
	for i := 1; i < n; i++ {
		if nums[i] != nums[end] {
			end++
			nums[end] = nums[i]
		}
	}
	return end + 1
}
<commit_msg>add explain<commit_after>/* https://leetcode.com/problems/remove-duplicates-from-sorted-array/#/description
Given a sorted array, remove the duplicates in place such that each element appear only once and return the new length.

Do not allocate extra space for another array, you must do this in place with constant memory.

For example,
Given input array nums = [1,1,2],

Your function should return length = 2, with the first two elements of nums being 1 and 2 respectively. It doesn't matter what you leave beyond the new length.
注意：nums需要处理一下，也就是不重复的元素要在前面，重复的需要挪到后面
*/
package leetcode

func removeDuplicates(nums []int) int {
	n, end := len(nums), 0
	if n < 2 {
		return n
	}
	for i := 1; i < n; i++ {
		if nums[end] != nums[i] {
			nums[end] = nums[i]
			end++
		}
	}
	return end + 1
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"strings"
	"sync"

	"tinygo.org/x/bluetooth"
)

var KnownServiceUUIDs = []bluetooth.UUID{
	bluetooth.ServiceUUIDCyclingSpeedAndCadence,
	bluetooth.ServiceUUIDCyclingPower,
	bluetooth.ServiceUUIDHeartRate,

	// General controllable device, seems more involved.
	// bluetooth.ServiceUUIDFitnessMachine,
}

var KnownServiceCharacteristicUUIDs = map[bluetooth.UUID][]bluetooth.UUID{
	// https://www.bluetooth.com/specifications/specs/cycling-power-service-1-1/
	bluetooth.ServiceUUIDCyclingPower: {
		bluetooth.CharacteristicUUIDCyclingPowerMeasurement,
		bluetooth.CharacteristicUUIDCyclingPowerFeature,
		// TODO:
		// Not a standardized characteristic, but this is offered by KICKR.
		// See GoldenCheetah source for some use examples:
		// https://github.com/GoldenCheetah/GoldenCheetah/blob/master/src/Train/BT40Device.cpp
		//
		// var WahooKickrControlCharacteristic = bluetooth.ParseUUID(
		// 	"a026e005-0a7d-4ab3-97fa-f1500f9feb8b"
		// )
		// TODO: Also, how does this one work?
		// bluetooth.CharacteristicUUIDCyclingPowerControlPoint,
	},
	bluetooth.ServiceUUIDHeartRate: {
		bluetooth.CharacteristicUUIDHeartRateMeasurement,
	},
}
var (
	KnownServiceNames = map[bluetooth.UUID]string{
		bluetooth.ServiceUUIDCyclingSpeedAndCadence: "Cycling Speed and Cadence",
		bluetooth.ServiceUUIDCyclingPower:           "Cycling Power",
		bluetooth.ServiceUUIDHeartRate:              "Heart Rate",
	}
	KnownCharacteristicNames = map[bluetooth.UUID]string{
		bluetooth.CharacteristicUUIDCyclingPowerMeasurement: "Cycling Power Measure",
		bluetooth.CharacteristicUUIDCyclingPowerFeature:     "Cycling Power Feature",
		bluetooth.CharacteristicUUIDHeartRateMeasurement:    "Heart Rate Measurement",
	}
)

type MetricKind int

const (
	MetricHeartRate = iota
	MetricCyclingPower
	MetricCyclingSpeed
	MetricCyclingCadence
)

type DeviceMetric struct {
	kind  MetricKind
	value int
}

type MetricSink struct {
}

type MetricSource struct {
	sinks []chan DeviceMetric

	svc *bluetooth.DeviceService
	ch  *bluetooth.DeviceCharacteristic
}

func NewMetricSource(
	svc *bluetooth.DeviceService,
	ch *bluetooth.DeviceCharacteristic,
) MetricSource {
	return MetricSource{
		sinks: []chan DeviceMetric{},
		svc:   svc,
		ch:    ch,
	}
}

func (src *MetricSource) Name() string {
	if name, ok := KnownCharacteristicNames[src.ch.UUID()]; ok {
		return name
	}
	return fmt.Sprintf("<unknown: %s>", src.ch.UUID().String())
}

func (src *MetricSource) AddSink(sink chan DeviceMetric) {
	src.sinks = append(src.sinks, sink)

	// Start listenening first time we add a sink
	if len(src.sinks) == 1 {
		handler := src.notificationHandler()
		src.ch.EnableNotifications(handler)
	}
}

func (src *MetricSource) notificationHandler() func([]byte) {
	switch src.ch.UUID() {
	case bluetooth.CharacteristicUUIDCyclingPowerMeasurement:
		return src.handleCyclingPowerMeasurement

	// TODO
	case bluetooth.CharacteristicUUIDCyclingPowerFeature:
	case bluetooth.CharacteristicUUIDHeartRateMeasurement:
		return src.handleHeartRateMeasurement
	}

	return nil
}

func (src *MetricSource) emit(m DeviceMetric) {
	for _, sink := range src.sinks {
		sink <- m
	}
}

const (
	// BPM size, 0 if u8, 1 if u16
	HeartRateFlagSize = 1 << 0

	// 00 unsupported
	// 01 unsupported
	// 10 supported, not detected
	// 11 supported, detected
	HeartRateFlagContactStatus = (1 << 1) | (1 << 2)

	HeartRateFlagHasEnergyExpended = 1 << 3
	HeartRateFlagHasRRInterval     = 1 << 4

	// bits 5-8 reserved
)

func (src *MetricSource) handleHeartRateMeasurement(buf []byte) {
	// malformed
	if len(buf) < 2 {
		return
	}

	flag := buf[0]

	is16Bit := (flag & HeartRateFlagSize) != 0
	contactStatus := (flag & HeartRateFlagContactStatus) >> 1

	contactSupported := contactStatus&(0b10) != 0
	contactFound := contactStatus&(0b01) != 0

	// No use sending this metric if the sensor isn't reading.
	if contactSupported && !contactFound {
		return
	}

	var hr int = int(buf[1])
	if is16Bit {
		hr = (hr << 8) | int(buf[2])
	}

	src.emit(DeviceMetric{
		kind:  MetricHeartRate,
		value: hr,
	})
}

const (
	CyclingPowerFlagHasPedalPowerBalance       = 1 << 0
	CyclingPowerFlagPedalPowerBalanceReference = 1 << 1
	CyclingPowerFlagHasAccumulatedTorque       = 1 << 2
	CyclingPowerFlagAccumulatedTorqueSource    = 1 << 3
	CyclingPowerFlagHasWheelRevolution         = 1 << 4
	CyclingPowerFlagHasCrankRevolution         = 1 << 5
	CyclingPowerFlagHasExtremeForceMagnitudes  = 1 << 6
	CyclingPowerFlagHasExtremeTorqueMagnitudes = 1 << 7
)

// Packet is [FLAG BYTE] [POWER WATTS]
func (src *MetricSource) handleCyclingPowerMeasurement(buf []byte) {
	// fmt.Printf("%s: got %+v\n", src.Name(), buf)
}

func scanDevices() {
	adapter := bluetooth.DefaultAdapter
	fmt.Println("Starting device scan...")

	if err := adapter.Enable(); err != nil {
		fmt.Println("FATAL: Failed to enable BLE")
		panic(err)
	}

	// Keep track of addresses we've already looked ad
	addrsChecked := map[string]bool{}

	onScanResult := func(bt *bluetooth.Adapter, result bluetooth.ScanResult) {
		if _, seen := addrsChecked[result.Address.String()]; seen {
			return
		}
		addrsChecked[result.Address.String()] = true

		serviceNames := []string{}
		for _, s := range KnownServiceUUIDs {
			if !result.HasServiceUUID(s) {
				continue
			}

			serviceNames = append(serviceNames, KnownServiceNames[s])
		}

		// No matching services, skip this device.
		if len(serviceNames) == 0 {
			return
		}

		fmt.Printf("%s %-20s %-20s [RSSI:%d]\n",
			result.Address.String(),
			result.LocalName(),
			strings.Join(serviceNames, ","),
			result.RSSI,
		)
	}

	if err := adapter.Scan(onScanResult); err != nil {
		fmt.Println("FATAL: Failed to scan for devices")
		panic(err)
	}

	fmt.Println("Scan complete.")
}

var (
	flagScan                    = flag.Bool("scan", false, "scan for nearby devices")
	flagHeartRateAddr           = flag.String("hr", "", "address for heart rate device")
	flagCyclingPowerAddr        = flag.String("power", "", "address for cycling power device")
	flagCyclingSpeedCadenceAddr = flag.String("speed", "", "address for cycling speed/cadence device")
)

func init() {
	flag.Parse()
}

func main() {
	if *flagScan {
		scanDevices()
		return
	}

	adapter := bluetooth.DefaultAdapter
	if err := adapter.Enable(); err != nil {
		fmt.Println("FATAL: Failed to enable BLE")
		panic(err)
	}

	deviceChan := make(chan *bluetooth.Device)

	wg := sync.WaitGroup{}

	connectRetry := func(addr string) {
		uuid, err := bluetooth.ParseUUID(addr)
		if err != nil {
			fmt.Printf("FATAL: bad UUID given: <%s>\n", addr)
			panic(err)
		}

		cp := bluetooth.ConnectionParams{}
		for {
			// TODO: bluetooth.Address bit is not cross-platform.
			device, err := adapter.Connect(bluetooth.Address{uuid}, cp)
			if err != nil {
				fmt.Printf("WARN: connect to <%s> failed: %+v\n", addr, err)
				continue
			}

			deviceChan <- device
			break
		}

		wg.Done()
	}

	if *flagHeartRateAddr != "" {
		wg.Add(1)
		go connectRetry(*flagHeartRateAddr)
	}
	if *flagCyclingPowerAddr != "" {
		wg.Add(1)
		go connectRetry(*flagCyclingPowerAddr)
	}
	if *flagCyclingSpeedCadenceAddr != "" {
		wg.Add(1)
		go connectRetry(*flagCyclingSpeedCadenceAddr)
	}

	go func() {
		wg.Wait()
		close(deviceChan)
	}()

	metricsChan := make(chan DeviceMetric)
	go func() {
		for m := range metricsChan {
			fmt.Printf("Metric: %+v\n", m)
		}
	}()

	for device := range deviceChan {
		fmt.Println("Initializing device...")
		services, err := device.DiscoverServices(KnownServiceUUIDs)
		if err != nil {
			panic(err)
		}

		for _, service := range services {
			if name, ok := KnownServiceNames[service.UUID()]; ok {
				fmt.Printf("\tservice: %s\n", name)
			} else {
				fmt.Printf("\tservice: unknown <%+v>\n", service.UUID().String())
			}

			knownChars := KnownServiceCharacteristicUUIDs[service.UUID()]
			chars, err := service.DiscoverCharacteristics(knownChars)
			if err != nil {
				panic(err)
			}

			for _, char := range chars {
				name := KnownCharacteristicNames[char.UUID()]
				fmt.Printf("\t\tcharacteristic: %s\n", name)

				src := NewMetricSource(&service, &char)
				src.AddSink(metricsChan)
			}
		}
	}

	println("that's all!")
	select {}
}
<commit_msg>Add cycling power measurement documentation<commit_after>package main

import (
	"flag"
	"fmt"
	"strings"
	"sync"

	"tinygo.org/x/bluetooth"
)

var KnownServiceUUIDs = []bluetooth.UUID{
	bluetooth.ServiceUUIDCyclingSpeedAndCadence,
	bluetooth.ServiceUUIDCyclingPower,
	bluetooth.ServiceUUIDHeartRate,

	// General controllable device, seems more involved.
	// bluetooth.ServiceUUIDFitnessMachine,
}

var KnownServiceCharacteristicUUIDs = map[bluetooth.UUID][]bluetooth.UUID{
	// https://www.bluetooth.com/specifications/specs/cycling-power-service-1-1/
	bluetooth.ServiceUUIDCyclingPower: {
		bluetooth.CharacteristicUUIDCyclingPowerMeasurement,
		bluetooth.CharacteristicUUIDCyclingPowerFeature,
		// TODO:
		// Not a standardized characteristic, but this is offered by KICKR.
		// See GoldenCheetah source for some use examples:
		// https://github.com/GoldenCheetah/GoldenCheetah/blob/master/src/Train/BT40Device.cpp
		//
		// var WahooKickrControlCharacteristic = bluetooth.ParseUUID(
		// 	"a026e005-0a7d-4ab3-97fa-f1500f9feb8b"
		// )
		// TODO: Also, how does this one work?
		// bluetooth.CharacteristicUUIDCyclingPowerControlPoint,
	},
	bluetooth.ServiceUUIDHeartRate: {
		bluetooth.CharacteristicUUIDHeartRateMeasurement,
	},
}
var (
	KnownServiceNames = map[bluetooth.UUID]string{
		bluetooth.ServiceUUIDCyclingSpeedAndCadence: "Cycling Speed and Cadence",
		bluetooth.ServiceUUIDCyclingPower:           "Cycling Power",
		bluetooth.ServiceUUIDHeartRate:              "Heart Rate",
	}
	KnownCharacteristicNames = map[bluetooth.UUID]string{
		bluetooth.CharacteristicUUIDCyclingPowerMeasurement: "Cycling Power Measure",
		bluetooth.CharacteristicUUIDCyclingPowerFeature:     "Cycling Power Feature",
		bluetooth.CharacteristicUUIDHeartRateMeasurement:    "Heart Rate Measurement",
	}
)

type MetricKind int

const (
	MetricHeartRate = iota
	MetricCyclingPower
	MetricCyclingSpeed
	MetricCyclingCadence
)

type DeviceMetric struct {
	kind  MetricKind
	value int
}

type MetricSink struct {
}

type MetricSource struct {
	sinks []chan DeviceMetric

	svc *bluetooth.DeviceService
	ch  *bluetooth.DeviceCharacteristic
}

func NewMetricSource(
	svc *bluetooth.DeviceService,
	ch *bluetooth.DeviceCharacteristic,
) MetricSource {
	return MetricSource{
		sinks: []chan DeviceMetric{},
		svc:   svc,
		ch:    ch,
	}
}

func (src *MetricSource) Name() string {
	if name, ok := KnownCharacteristicNames[src.ch.UUID()]; ok {
		return name
	}
	return fmt.Sprintf("<unknown: %s>", src.ch.UUID().String())
}

func (src *MetricSource) AddSink(sink chan DeviceMetric) {
	src.sinks = append(src.sinks, sink)

	// Start listenening first time we add a sink
	if len(src.sinks) == 1 {
		handler := src.notificationHandler()
		src.ch.EnableNotifications(handler)
	}
}

func (src *MetricSource) notificationHandler() func([]byte) {
	switch src.ch.UUID() {
	case bluetooth.CharacteristicUUIDCyclingPowerMeasurement:
		return src.handleCyclingPowerMeasurement

	// TODO
	case bluetooth.CharacteristicUUIDCyclingPowerFeature:
	case bluetooth.CharacteristicUUIDHeartRateMeasurement:
		return src.handleHeartRateMeasurement
	}

	return nil
}

func (src *MetricSource) emit(m DeviceMetric) {
	for _, sink := range src.sinks {
		sink <- m
	}
}

const (
	// BPM size, 0 if u8, 1 if u16
	HeartRateFlagSize = 1 << 0

	// 00 unsupported
	// 01 unsupported
	// 10 supported, not detected
	// 11 supported, detected
	HeartRateFlagContactStatus = (1 << 1) | (1 << 2)

	HeartRateFlagHasEnergyExpended = 1 << 3
	HeartRateFlagHasRRInterval     = 1 << 4

	// bits 5-8 reserved
)

func (src *MetricSource) handleHeartRateMeasurement(buf []byte) {
	// malformed
	if len(buf) < 2 {
		return
	}

	flag := buf[0]

	is16Bit := (flag & HeartRateFlagSize) != 0
	contactStatus := (flag & HeartRateFlagContactStatus) >> 1

	contactSupported := contactStatus&(0b10) != 0
	contactFound := contactStatus&(0b01) != 0

	// No use sending this metric if the sensor isn't reading.
	if contactSupported && !contactFound {
		return
	}

	var hr int = int(buf[1])
	if is16Bit {
		hr = (hr << 8) | int(buf[2])
	}

	src.emit(DeviceMetric{
		kind:  MetricHeartRate,
		value: hr,
	})
}

const (
	CyclingPowerFlagHasPedalPowerBalance           = 1 << 0
	CyclingPowerFlagPedalPowerBalanceReference     = 1 << 1
	CyclingPowerFlagHasAccumulatedTorque           = 1 << 2
	CyclingPowerFlagAccumulatedTorqueSource        = 1 << 3
	CyclingPowerFlagHasWheelRevolution             = 1 << 4
	CyclingPowerFlagHasCrankRevolution             = 1 << 5
	CyclingPowerFlagHasExtremeForceMagnitudes      = 1 << 6
	CyclingPowerFlagHasExtremeTorqueMagnitudes     = 1 << 7
	CyclingPowerFlagHasExtremeAngles               = 1 << 8
	CyclingPowerFlagHasTopDeadSpotAngle            = 1 << 9
	CyclingPowerFlagHasBottomDeadSpotAngle         = 1 << 10
	CyclingPowerFlagHasAccumulatedEnergy           = 1 << 11
	CyclingPowerFlagHasOffsetCompensationIndicator = 1 << 12

	// Bits 13-16 reserved
)

// Two flag bytes, followed by a 16 bit power reading. All subsequent
// fields are optional, based on the flag bits set.
//
// sint16  instantaneous_power      watts with resolution 1
// uint8   pedal_power_balance      percentage with resolution 1/2
// uint16  accumulated_torque       newton meters with resolution 1/32
// uint32  wheel_rev_cumulative     unitless
// uint16  wheel_rev_last_time      seconds with resolution 1/2048
// uint16  crank_rev_cumulative     unitless
// uint16  crank_rev_last_time      seconds with resolution 1/1024
// sint16  extreme_force_max_magn   newtons with resolution 1
// sint16  extreme_force_min_magn   newtons with resolution 1
// sint16  extreme_torque_max_magn  newton meters with resolution 1/32
// sint16  extreme_torque_min_magn  newton meters with resolution 1/32
// uint12  extreme_angles_max       degrees with resolution 1
// uint12  extreme_angles_min       degrees with resolution 1
// uint16  top_dead_spot_angle      degrees with resolution 1
// uint16  bottom_dead_spot_angle   degrees with resolution 1
// uint16  accumulated_energy       kilojoules with resolution 1
func (src *MetricSource) handleCyclingPowerMeasurement(buf []byte) {
	// malformed
	if len(buf) < 2 {
		return
	}

	flags := uint16(buf[0]) | uint16(buf[1]<<8)

	watts := int16(buf[2]<<8) | int16(buf[3])
	fmt.Printf("power measure: %d watts, flags=%b\n", watts, flags)

	if flags&CyclingPowerFlagHasAccumulatedEnergy != 0 {
		fmt.Println("also have energy")
	}

}

func scanDevices() {
	adapter := bluetooth.DefaultAdapter
	fmt.Println("Starting device scan...")

	if err := adapter.Enable(); err != nil {
		fmt.Println("FATAL: Failed to enable BLE")
		panic(err)
	}

	// Keep track of addresses we've already looked ad
	addrsChecked := map[string]bool{}

	onScanResult := func(bt *bluetooth.Adapter, result bluetooth.ScanResult) {
		if _, seen := addrsChecked[result.Address.String()]; seen {
			return
		}
		addrsChecked[result.Address.String()] = true

		serviceNames := []string{}
		for _, s := range KnownServiceUUIDs {
			if !result.HasServiceUUID(s) {
				continue
			}

			serviceNames = append(serviceNames, KnownServiceNames[s])
		}

		// No matching services, skip this device.
		if len(serviceNames) == 0 {
			return
		}

		fmt.Printf("%s %-20s %-20s [RSSI:%d]\n",
			result.Address.String(),
			result.LocalName(),
			strings.Join(serviceNames, ","),
			result.RSSI,
		)
	}

	if err := adapter.Scan(onScanResult); err != nil {
		fmt.Println("FATAL: Failed to scan for devices")
		panic(err)
	}

	fmt.Println("Scan complete.")
}

var (
	flagScan                    = flag.Bool("scan", false, "scan for nearby devices")
	flagHeartRateAddr           = flag.String("hr", "", "address for heart rate device")
	flagCyclingPowerAddr        = flag.String("power", "", "address for cycling power device")
	flagCyclingSpeedCadenceAddr = flag.String("speed", "", "address for cycling speed/cadence device")
)

func init() {
	flag.Parse()
}

func main() {
	if *flagScan {
		scanDevices()
		return
	}

	adapter := bluetooth.DefaultAdapter
	if err := adapter.Enable(); err != nil {
		fmt.Println("FATAL: Failed to enable BLE")
		panic(err)
	}

	deviceChan := make(chan *bluetooth.Device)

	wg := sync.WaitGroup{}

	connectRetry := func(addr string) {
		uuid, err := bluetooth.ParseUUID(addr)
		if err != nil {
			fmt.Printf("FATAL: bad UUID given: <%s>\n", addr)
			panic(err)
		}

		cp := bluetooth.ConnectionParams{}
		for {
			// TODO: bluetooth.Address bit is not cross-platform.
			device, err := adapter.Connect(bluetooth.Address{uuid}, cp)
			if err != nil {
				fmt.Printf("WARN: connect to <%s> failed: %+v\n", addr, err)
				continue
			}

			deviceChan <- device
			break
		}

		wg.Done()
	}

	if *flagHeartRateAddr != "" {
		wg.Add(1)
		go connectRetry(*flagHeartRateAddr)
	}
	if *flagCyclingPowerAddr != "" {
		wg.Add(1)
		go connectRetry(*flagCyclingPowerAddr)
	}
	if *flagCyclingSpeedCadenceAddr != "" {
		wg.Add(1)
		go connectRetry(*flagCyclingSpeedCadenceAddr)
	}

	go func() {
		wg.Wait()
		close(deviceChan)
	}()

	metricsChan := make(chan DeviceMetric)
	go func() {
		for m := range metricsChan {
			fmt.Printf("Metric: %+v\n", m)
		}
	}()

	for device := range deviceChan {
		fmt.Println("Initializing device...")
		services, err := device.DiscoverServices(KnownServiceUUIDs)
		if err != nil {
			panic(err)
		}

		for _, service := range services {
			if name, ok := KnownServiceNames[service.UUID()]; ok {
				fmt.Printf("\tservice: %s\n", name)
			} else {
				fmt.Printf("\tservice: unknown <%+v>\n", service.UUID().String())
			}

			knownChars := KnownServiceCharacteristicUUIDs[service.UUID()]
			chars, err := service.DiscoverCharacteristics(knownChars)
			if err != nil {
				panic(err)
			}

			for _, char := range chars {
				name := KnownCharacteristicNames[char.UUID()]
				fmt.Printf("\t\tcharacteristic: %s\n", name)

				src := NewMetricSource(&service, &char)
				src.AddSink(metricsChan)
			}
		}
	}

	println("that's all!")
	select {}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/rpc"
	"net/rpc/jsonrpc"
	"os"
	"net"

	"github.com/nofdev/fastforward/provisioning"
)

// Args struct will be the json args
type Args struct {
	User string
	Host string
	Output bool
	Abort bool
	provisioning.Cmd
}

type Api int
var i provisioning.Provisioning

// Execute command from api
func (a *Api) Exec(args *Args, reply *string) error {
	c, err := provisioning.MakeConfig(args.User, args.Host, args.Output, args.Abort)
	checkError(err)

	i = c
	i.Execute(args.Cmd)
	return nil
}

func main() {
	provision := new(Api)
	rpc.Register(provision)

	tcpAddr, err := net.ResolveTCPAddr("tcp", ":8080")
	checkError(err)
	listener, err := net.ListenTCP("tcp", tcpAddr)
	checkError(err)

	for {
		conn, err := listener.Accept()
		if err != nil {
			continue
		}
		jsonrpc.ServeConn(conn)
	}
}

func checkError(err error) {
	if err != nil {
		fmt.Println("Fatal error ", err.Error())
		os.Exit(1)
	}
}
<commit_msg>Add version 1 api for provisioning<commit_after>package main

import (
    "github.com/gorilla/mux"
    "github.com/gorilla/rpc"
    "github.com/gorilla/rpc/json"
    "log"
    "net/http"
    "github.com/nofdev/fastforward/provisioning"
)

type Config struct {
    provisioning.Conf
}

type Args struct {
    provisioning.Conf
    provisioning.Cmd
}

type Result interface {}

func (this *Config) Exec(r *http.Request, args *Args, result *Result) error {

	c, err := provisioning.MakeConfig(args.User, args.Host, args.DisplayOutput, args.AbortOnError); if err != nil {
		log.Printf("Make config error, %s", err)
	}
	cmd := provisioning.Cmd{AptCache: args.AptCache, UseSudo: args.UseSudo, CmdLine: args.CmdLine}

	var i provisioning.Provisioning
	i = c
	i.Execute(cmd)
    return nil
}

func main() {
    s := rpc.NewServer()
    s.RegisterCodec(json.NewCodec(), "application/json")
    s.RegisterCodec(json.NewCodec(), "application/json;charset=UTF-8")
    config := new(Config)
    s.RegisterService(config, "")
    r := mux.NewRouter()
    r.Handle("/v1", s)
    http.ListenAndServe(":7000", r)
}<|endoftext|>


<commit_before>package emailnotifier

import (
	"errors"
	"fmt"
	"koding/db/mongodb/modelhelper"
	socialmodels "socialapi/models"
	"socialapi/workers/notification/models"

	"github.com/koding/logging"
	"github.com/koding/rabbitmq"
	"github.com/koding/worker"
	"github.com/sendgrid/sendgrid-go"
	"github.com/streadway/amqp"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
)

var emailConfig = map[string]string{
	models.NotificationContent_TYPE_COMMENT: "comment",
	models.NotificationContent_TYPE_LIKE:    "likeActivities",
	models.NotificationContent_TYPE_FOLLOW:  "followActions",
	models.NotificationContent_TYPE_JOIN:    "groupJoined",
	models.NotificationContent_TYPE_LEAVE:   "groupLeft",
	models.NotificationContent_TYPE_MENTION: "mention",
}

type Action func(*Controller, []byte) error

type Controller struct {
	routes   map[string]Action
	log      logging.Logger
	rmqConn  *amqp.Connection
	settings *EmailSettings
}

type EmailSettings struct {
	Username string
	Password string
	FromName string
	FromMail string
}

func (n *Controller) DefaultErrHandler(delivery amqp.Delivery, err error) bool {
	n.log.Error("an error occured: %s", err)
	delivery.Ack(false)

	return false
}

func (n *Controller) HandleEvent(event string, data []byte) error {
	n.log.Debug("New Event Received %s", event)
	handler, ok := n.routes[event]
	if !ok {
		return worker.HandlerNotFoundErr
	}

	return handler(n, data)
}

func New(rmq *rabbitmq.RabbitMQ, log logging.Logger, es *EmailSettings) (*Controller, error) {
	rmqConn, err := rmq.Connect("NewEmailNotifierWorkerController")
	if err != nil {
		return nil, err
	}

	nwc := &Controller{
		log:      log,
		rmqConn:  rmqConn.Conn(),
		settings: es,
	}

	routes := map[string]Action{
		"notification.notification_created": (*Controller).SendInstantEmail,
		"notification.notification_updated": (*Controller).SendInstantEmail,
	}

	nwc.routes = routes

	return nwc, nil
}

func (n *Controller) SendInstantEmail(data []byte) error {
	channel, err := n.rmqConn.Channel()
	if err != nil {
		return errors.New("channel connection error")
	}
	defer channel.Close()

	notification := models.NewNotification()
	if err := notification.MapMessage(data); err != nil {
		return err
	}

	// fetch latest activity for checking actor
	activity, nc, err := notification.FetchLastActivity()
	if err != nil {
		return err
	}

	if !validNotification(activity, notification) {
		return nil
	}

	uc, err := fetchUserContact(notification.AccountId)
	if err != nil {
		return fmt.Errorf("an error occurred while fetching user contact: %s", err)
	}

	if !checkMailSettings(uc, nc) {
		return nil
	}

	container, err := buildContainer(activity, nc, notification)
	if err != nil {
		return err
	}

	body, err := renderTemplate(uc, container)
	if err != nil {
		return fmt.Errorf("an error occurred while preparing notification email: %s", err)
	}
	subject := prepareSubject(container)

	if err := createToken(uc, nc, container.Token); err != nil {
		return err
	}

	return n.SendMail(uc, body, subject)
}

type UserContact struct {
	UserOldId     bson.ObjectId
	Email         string
	FirstName     string
	LastName      string
	Username      string
	Hash          string
	EmailSettings map[string]bool
}

func validNotification(a *models.NotificationActivity, n *models.Notification) bool {
	// do not notify actor for her own action
	if a.ActorId == n.AccountId {
		return false
	}

	// do not notify user when notification is not yet activated
	return !n.ActivatedAt.IsZero()
}

func checkMailSettings(uc *UserContact, nc *models.NotificationContent) bool {
	// notifications are disabled
	if val := uc.EmailSettings["global"]; !val {
		return false
	}

	// daily notifications are enabled
	if val := uc.EmailSettings["daily"]; val {
		return false
	}

	// get config
	return uc.EmailSettings[emailConfig[nc.TypeConstant]]
}

func buildContainer(a *models.NotificationActivity, nc *models.NotificationContent,
	n *models.Notification) (*NotificationContainer, error) {

	// if content type not valid return
	contentType, err := nc.GetContentType()
	if err != nil {
		return nil, err
	}

	container := &NotificationContainer{
		Activity:     a,
		Content:      nc,
		Notification: n,
	}

	container.Token, err = generateToken()
	if err != nil {
		return nil, err
	}

	// if notification target is related with an object (comment/status update)
	if containsObject(nc) {
		target := socialmodels.NewChannelMessage()
		if err := target.ById(nc.TargetId); err != nil {
			return nil, fmt.Errorf("target message not found")
		}

		prepareGroup(container, target)
		prepareSlug(container, target)
		prepareObjectType(container, target)
		container.Message = fetchContentBody(nc, target)
		contentType.SetActorId(target.AccountId)
		contentType.SetListerId(n.AccountId)
	}

	container.ActivityMessage = contentType.GetActivity()

	return container, nil
}

func prepareGroup(container *NotificationContainer, cm *socialmodels.ChannelMessage) {
	c := socialmodels.NewChannel()
	if err := c.ById(cm.InitialChannelId); err != nil {
		return
	}
	// TODO fix these Slug and Name
	container.Group = GroupContent{
		Slug: c.GroupName,
		Name: c.GroupName,
	}
}

func prepareSlug(container *NotificationContainer, cm *socialmodels.ChannelMessage) {
	switch cm.TypeConstant {
	case socialmodels.ChannelMessage_TYPE_POST:
		container.Slug = cm.Slug
	case socialmodels.ChannelMessage_TYPE_REPLY:
		// TODO we need append something like comment id to parent message slug
		container.Slug = fetchRepliedMessage(cm.Id).Slug
	}
}

func prepareObjectType(container *NotificationContainer, cm *socialmodels.ChannelMessage) {
	switch cm.TypeConstant {
	case socialmodels.ChannelMessage_TYPE_POST:
		container.ObjectType = "status update"
	case socialmodels.ChannelMessage_TYPE_REPLY:
		container.ObjectType = "comment"
	}
}

// fetchUserContact gets user and account details with given account id
func fetchUserContact(accountId int64) (*UserContact, error) {
	a := socialmodels.NewAccount()
	if err := a.ById(accountId); err != nil {
		return nil, err
	}

	account, err := modelhelper.GetAccountById(a.OldId)
	if err != nil {
		if err == mgo.ErrNotFound {
			return nil, errors.New("old account not found")
		}

		return nil, err
	}

	user, err := modelhelper.GetUser(account.Profile.Nickname)
	if err != nil {
		if err == mgo.ErrNotFound {
			return nil, errors.New("user not found")
		}

		return nil, err
	}

	uc := &UserContact{
		UserOldId:     user.ObjectId,
		Email:         user.Email,
		FirstName:     account.Profile.FirstName,
		LastName:      account.Profile.LastName,
		Username:      account.Profile.Nickname,
		Hash:          account.Profile.Hash,
		EmailSettings: user.EmailFrequency,
	}

	return uc, nil
}

func containsObject(nc *models.NotificationContent) bool {
	return nc.TypeConstant == models.NotificationContent_TYPE_LIKE ||
		nc.TypeConstant == models.NotificationContent_TYPE_MENTION ||
		nc.TypeConstant == models.NotificationContent_TYPE_COMMENT
}

func fetchContentBody(nc *models.NotificationContent, cm *socialmodels.ChannelMessage) string {

	switch nc.TypeConstant {
	case models.NotificationContent_TYPE_LIKE:
		return cm.Body
	case models.NotificationContent_TYPE_MENTION:
		return cm.Body
	case models.NotificationContent_TYPE_COMMENT:
		return fetchLastReplyBody(cm.Id)
	}

	return ""
}

func fetchLastReplyBody(targetId int64) string {
	mr := socialmodels.NewMessageReply()
	mr.MessageId = targetId
	query := socialmodels.NewQuery()
	query.Limit = 1
	messages, err := mr.List(query)
	if err != nil {
		return ""
	}

	if len(messages) == 0 {
		return ""
	}

	return messages[0].Body
}

func fetchRepliedMessage(replyId int64) *socialmodels.ChannelMessage {
	mr := socialmodels.NewMessageReply()
	mr.ReplyId = replyId

	parent, err := mr.FetchRepliedMessage()
	if err != nil {
		parent = socialmodels.NewChannelMessage()
	}

	return parent
}

func (n *Controller) SendMail(uc *UserContact, body, subject string) error {
	es := n.settings
	sg := sendgrid.NewSendGridClient(es.Username, es.Password)
	fullname := fmt.Sprintf("%s %s", uc.FirstName, uc.LastName)

	message := sendgrid.NewMail()
	message.AddTo(uc.Email)
	message.AddToName(fullname)
	message.SetSubject(subject)
	message.SetHTML(body)
	message.SetFrom(es.FromMail)
	message.SetFromName(es.FromName)

	if err := sg.Send(message); err != nil {
		return fmt.Errorf("an error occurred while sending notification email to %s", uc.Username)
	}
	n.log.Info("%s notified by email", uc.Username)

	return nil
}
<commit_msg>Notification: cron job is cheduled for sending daily notification emails<commit_after>package emailnotifier

import (
	"errors"
	"fmt"
	"koding/db/mongodb/modelhelper"
	socialmodels "socialapi/models"
	"socialapi/workers/notification/models"

	"github.com/koding/logging"
	"github.com/koding/rabbitmq"
	"github.com/koding/worker"
	"github.com/robfig/cron"
	"github.com/sendgrid/sendgrid-go"
	"github.com/streadway/amqp"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
)

const SCHEDULE = "0 0 0 * * *"

var cronJob *cron.Cron

var emailConfig = map[string]string{
	models.NotificationContent_TYPE_COMMENT: "comment",
	models.NotificationContent_TYPE_LIKE:    "likeActivities",
	models.NotificationContent_TYPE_FOLLOW:  "followActions",
	models.NotificationContent_TYPE_JOIN:    "groupJoined",
	models.NotificationContent_TYPE_LEAVE:   "groupLeft",
	models.NotificationContent_TYPE_MENTION: "mention",
}

type Action func(*Controller, []byte) error

type Controller struct {
	routes   map[string]Action
	log      logging.Logger
	rmqConn  *amqp.Connection
	settings *EmailSettings
}

type EmailSettings struct {
	Username string
	Password string
	FromName string
	FromMail string
}

func (n *Controller) DefaultErrHandler(delivery amqp.Delivery, err error) bool {
	n.log.Error("an error occured: %s", err)
	delivery.Ack(false)

	return false
}

func (n *Controller) HandleEvent(event string, data []byte) error {
	n.log.Debug("New Event Received %s", event)
	handler, ok := n.routes[event]
	if !ok {
		return worker.HandlerNotFoundErr
	}

	return handler(n, data)
}

func New(rmq *rabbitmq.RabbitMQ, log logging.Logger, es *EmailSettings) (*Controller, error) {
	rmqConn, err := rmq.Connect("NewEmailNotifierWorkerController")
	if err != nil {
		return nil, err
	}

	nwc := &Controller{
		log:      log,
		rmqConn:  rmqConn.Conn(),
		settings: es,
	}

	routes := map[string]Action{
		"notification.notification_created": (*Controller).SendInstantEmail,
		"notification.notification_updated": (*Controller).SendInstantEmail,
	}

	nwc.routes = routes

	nwc.initDailyEmailCron()

	return nwc, nil
}

func (n *EmailNotifierWorkerController) initDailyEmailCron() {

	cronJob = cron.New()
	cronJob.AddFunc(SCHEDULE, n.sendDailyMails)
	cronJob.Start()
}

func (n *Controller) SendInstantEmail(data []byte) error {
	channel, err := n.rmqConn.Channel()
	if err != nil {
		return errors.New("channel connection error")
	}
	defer channel.Close()

	notification := models.NewNotification()
	if err := notification.MapMessage(data); err != nil {
		return err
	}

	// fetch latest activity for checking actor
	activity, nc, err := notification.FetchLastActivity()
	if err != nil {
		return err
	}

	if !validNotification(activity, notification) {
		return nil
	}

	uc, err := fetchUserContact(notification.AccountId)
	if err != nil {
		return fmt.Errorf("an error occurred while fetching user contact: %s", err)
	}

	if !checkMailSettings(uc, nc) {
		return nil
	}

	container, err := buildContainer(activity, nc, notification)
	if err != nil {
		return err
	}

	body, err := renderTemplate(uc, container)
	if err != nil {
		return fmt.Errorf("an error occurred while preparing notification email: %s", err)
	}
	subject := prepareSubject(container)

	if err := createToken(uc, nc, container.Token); err != nil {
		return err
	}

	return n.SendMail(uc, body, subject)
}

type UserContact struct {
	UserOldId     bson.ObjectId
	Email         string
	FirstName     string
	LastName      string
	Username      string
	Hash          string
	EmailSettings map[string]bool
}

func validNotification(a *models.NotificationActivity, n *models.Notification) bool {
	// do not notify actor for her own action
	if a.ActorId == n.AccountId {
		return false
	}

	// do not notify user when notification is not yet activated
	return !n.ActivatedAt.IsZero()
}

func checkMailSettings(uc *UserContact, nc *models.NotificationContent) bool {
	// notifications are disabled
	if val := uc.EmailSettings["global"]; !val {
		return false
	}

	// daily notifications are enabled
	if val := uc.EmailSettings["daily"]; val {
		return false
	}

	// get config
	return uc.EmailSettings[emailConfig[nc.TypeConstant]]
}

func buildContainer(a *models.NotificationActivity, nc *models.NotificationContent,
	n *models.Notification) (*NotificationContainer, error) {

	// if content type not valid return
	contentType, err := nc.GetContentType()
	if err != nil {
		return nil, err
	}

	container := &NotificationContainer{
		Activity:     a,
		Content:      nc,
		Notification: n,
	}

	container.Token, err = generateToken()
	if err != nil {
		return nil, err
	}

	// if notification target is related with an object (comment/status update)
	if containsObject(nc) {
		target := socialmodels.NewChannelMessage()
		if err := target.ById(nc.TargetId); err != nil {
			return nil, fmt.Errorf("target message not found")
		}

		prepareGroup(container, target)
		prepareSlug(container, target)
		prepareObjectType(container, target)
		container.Message = fetchContentBody(nc, target)
		contentType.SetActorId(target.AccountId)
		contentType.SetListerId(n.AccountId)
	}

	container.ActivityMessage = contentType.GetActivity()

	return container, nil
}

func prepareGroup(container *NotificationContainer, cm *socialmodels.ChannelMessage) {
	c := socialmodels.NewChannel()
	if err := c.ById(cm.InitialChannelId); err != nil {
		return
	}
	// TODO fix these Slug and Name
	container.Group = GroupContent{
		Slug: c.GroupName,
		Name: c.GroupName,
	}
}

func prepareSlug(container *NotificationContainer, cm *socialmodels.ChannelMessage) {
	switch cm.TypeConstant {
	case socialmodels.ChannelMessage_TYPE_POST:
		container.Slug = cm.Slug
	case socialmodels.ChannelMessage_TYPE_REPLY:
		// TODO we need append something like comment id to parent message slug
		container.Slug = fetchRepliedMessage(cm.Id).Slug
	}
}

func prepareObjectType(container *NotificationContainer, cm *socialmodels.ChannelMessage) {
	switch cm.TypeConstant {
	case socialmodels.ChannelMessage_TYPE_POST:
		container.ObjectType = "status update"
	case socialmodels.ChannelMessage_TYPE_REPLY:
		container.ObjectType = "comment"
	}
}

// fetchUserContact gets user and account details with given account id
func fetchUserContact(accountId int64) (*UserContact, error) {
	a := socialmodels.NewAccount()
	if err := a.ById(accountId); err != nil {
		return nil, err
	}

	account, err := modelhelper.GetAccountById(a.OldId)
	if err != nil {
		if err == mgo.ErrNotFound {
			return nil, errors.New("old account not found")
		}

		return nil, err
	}

	user, err := modelhelper.GetUser(account.Profile.Nickname)
	if err != nil {
		if err == mgo.ErrNotFound {
			return nil, errors.New("user not found")
		}

		return nil, err
	}

	uc := &UserContact{
		UserOldId:     user.ObjectId,
		Email:         user.Email,
		FirstName:     account.Profile.FirstName,
		LastName:      account.Profile.LastName,
		Username:      account.Profile.Nickname,
		Hash:          account.Profile.Hash,
		EmailSettings: user.EmailFrequency,
	}

	return uc, nil
}

func containsObject(nc *models.NotificationContent) bool {
	return nc.TypeConstant == models.NotificationContent_TYPE_LIKE ||
		nc.TypeConstant == models.NotificationContent_TYPE_MENTION ||
		nc.TypeConstant == models.NotificationContent_TYPE_COMMENT
}

func fetchContentBody(nc *models.NotificationContent, cm *socialmodels.ChannelMessage) string {

	switch nc.TypeConstant {
	case models.NotificationContent_TYPE_LIKE:
		return cm.Body
	case models.NotificationContent_TYPE_MENTION:
		return cm.Body
	case models.NotificationContent_TYPE_COMMENT:
		return fetchLastReplyBody(cm.Id)
	}

	return ""
}

func fetchLastReplyBody(targetId int64) string {
	mr := socialmodels.NewMessageReply()
	mr.MessageId = targetId
	query := socialmodels.NewQuery()
	query.Limit = 1
	messages, err := mr.List(query)
	if err != nil {
		return ""
	}

	if len(messages) == 0 {
		return ""
	}

	return messages[0].Body
}

func fetchRepliedMessage(replyId int64) *socialmodels.ChannelMessage {
	mr := socialmodels.NewMessageReply()
	mr.ReplyId = replyId

	parent, err := mr.FetchRepliedMessage()
	if err != nil {
		parent = socialmodels.NewChannelMessage()
	}

	return parent
}

func (n *Controller) SendMail(uc *UserContact, body, subject string) error {
	es := n.settings
	sg := sendgrid.NewSendGridClient(es.Username, es.Password)
	fullname := fmt.Sprintf("%s %s", uc.FirstName, uc.LastName)

	message := sendgrid.NewMail()
	message.AddTo(uc.Email)
	message.AddToName(fullname)
	message.SetSubject(subject)
	message.SetHTML(body)
	message.SetFrom(es.FromMail)
	message.SetFromName(es.FromName)

	if err := sg.Send(message); err != nil {
		return fmt.Errorf("an error occurred while sending notification email to %s", uc.Username)
	}
	n.log.Info("%s notified by email", uc.Username)

	return nil
}
<|endoftext|>


<commit_before>package turbulence

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"time"
)

const million int64 = 1000000

type Client struct {
	baseURL          string
	operationTimeout time.Duration
	pollingInterval  time.Duration
}

type Response struct {
	ID                   string          `json:"ID"`
	ExecutionStartedAt   string          `json:"ExecutionStartedAt"`
	ExecutionCompletedAt string          `json:"ExecutionCompletedAt"`
	Events               []ResponseEvent `json:"Events"`
}

type ResponseEvent struct {
	Error                string `json:"Error"`
	ID                   string `json:"ID"`
	Type                 string `json:"Type"`
	DeploymentName       string `json:"DeploymentName"`
	JobName              string `json:"JobName"`
	JobNameMatch         string `json:"JobNameMatch"`
	JobIndex             int    `json:"JobIndex"`
	ExecutionStartedAt   string `json:"ExecutionStartedAt"`
	ExecutionCompletedAt string `json:"ExecutionCompletedAt"`
}

type deployment struct {
	Name string
	Jobs []job
}

type job struct {
	Name    string
	Indices []int
}

type killTask struct {
	Type string
}

type controlNetTask struct {
	Type    string
	Timeout string
	Delay   string
}

type command struct {
	Tasks       []interface{}
	Deployments []deployment
}

func NewClient(baseURL string, operationTimeout time.Duration, pollingInterval time.Duration) Client {
	return Client{
		baseURL:          baseURL,
		operationTimeout: operationTimeout,
		pollingInterval:  pollingInterval,
	}
}

func (c Client) Delay(deploymentName string, jobName string, indices []int, delay time.Duration, timeout time.Duration) (Response, error) {
	command := command{
		Tasks: []interface{}{
			controlNetTask{Type: "control-net",
				Timeout: fmt.Sprintf("%dms", timeout.Nanoseconds()/million),
				Delay:   fmt.Sprintf("%dms", delay.Nanoseconds()/million)}},
		Deployments: []deployment{{
			Name: deploymentName,
			Jobs: []job{{Name: jobName, Indices: indices}},
		}},
	}

	jsonCommand, err := json.Marshal(command)
	if err != nil {
		return Response{}, err
	}

	resp, err := c.makeRequest("POST", c.baseURL+"/api/v1/incidents", bytes.NewBuffer(jsonCommand))
	if err != nil {
		return Response{}, err
	}

	return c.pollControlNetStarted(resp.ID)
}

func (c Client) pollControlNetStarted(id string) (Response, error) {
	startTime := time.Now()
	for {
		turbulenceResponse, err := c.makeRequest("GET", fmt.Sprintf("%s/api/v1/incidents/%s", c.baseURL, id), nil)
		if err != nil {
			return turbulenceResponse, err
		}

		for _, event := range turbulenceResponse.Events {
			if event.Error != "" {
				return turbulenceResponse, errors.New(event.Error)
			}
			if event.Type == "ControlNet" && event.ExecutionStartedAt != "" {
				return turbulenceResponse, nil
			}
		}

		if time.Now().Sub(startTime) > c.operationTimeout {
			return turbulenceResponse, errors.New(fmt.Sprintf("Did not start control-net event in time: %d", c.operationTimeout))
		}

		time.Sleep(c.pollingInterval)
	}
}

func (c Client) KillIndices(deploymentName, jobName string, indices []int) error {
	command := command{
		Tasks: []interface{}{
			killTask{Type: "kill"},
		},
		Deployments: []deployment{{
			Name: deploymentName,
			Jobs: []job{{Name: jobName, Indices: indices}},
		}},
	}

	jsonCommand, err := json.Marshal(command)
	if err != nil {
		return err
	}

	turbulenceResponse, err := c.makeRequest("POST", c.baseURL+"/api/v1/incidents", bytes.NewBuffer(jsonCommand))
	if err != nil {
		return err
	}

	return c.pollRequestCompletedDeletingVM(turbulenceResponse.ID)
}

func (c Client) pollRequestCompletedDeletingVM(id string) error {
	startTime := time.Now()
	for {
		turbulenceResponse, err := c.makeRequest("GET", fmt.Sprintf("%s/api/v1/incidents/%s", c.baseURL, id), nil)
		if err != nil {
			return err
		}

		if turbulenceResponse.ExecutionCompletedAt != "" {
			if len(turbulenceResponse.Events) == 0 {
				return errors.New("There should at least be one Event in response from turbulence.")
			}

			for _, event := range turbulenceResponse.Events {
				if event.Error != "" {
					return errors.New(event.Error)
				}
			}

			return nil
		}

		if time.Now().Sub(startTime) > c.operationTimeout {
			return errors.New(fmt.Sprintf("Did not finish deleting VM in time: %d", c.operationTimeout))
		}

		time.Sleep(c.pollingInterval)
	}
}

func (c Client) makeRequest(method string, path string, body io.Reader) (Response, error) {
	request, err := http.NewRequest(method, path, body)
	if err != nil {
		return Response{}, err
	}

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}

	resp, err := client.Do(request)
	if err != nil {
		return Response{}, err
	}

	var turbulenceResponse Response
	err = json.NewDecoder(resp.Body).Decode(&turbulenceResponse)
	if err != nil {
		return Response{}, errors.New("Unable to decode turbulence response.")
	}

	return turbulenceResponse, nil
}

func (c Client) Incident(id string) (Response, error) {
	return c.makeRequest("GET", fmt.Sprintf("%s/api/v1/incidents/%s", c.baseURL, id), nil)
}
<commit_msg>Turbulence might leak creds from event logs<commit_after>package turbulence

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"time"
)

const million int64 = 1000000

type Client struct {
	baseURL          string
	operationTimeout time.Duration
	pollingInterval  time.Duration
}

type Response struct {
	ID                   string          `json:"ID"`
	ExecutionStartedAt   string          `json:"ExecutionStartedAt"`
	ExecutionCompletedAt string          `json:"ExecutionCompletedAt"`
	Events               []ResponseEvent `json:"Events"`
}

type ResponseEvent struct {
	Error                string `json:"Error"`
	ID                   string `json:"ID"`
	Type                 string `json:"Type"`
	DeploymentName       string `json:"DeploymentName"`
	JobName              string `json:"JobName"`
	JobNameMatch         string `json:"JobNameMatch"`
	JobIndex             int    `json:"JobIndex"`
	ExecutionStartedAt   string `json:"ExecutionStartedAt"`
	ExecutionCompletedAt string `json:"ExecutionCompletedAt"`
}

type deployment struct {
	Name string
	Jobs []job
}

type job struct {
	Name    string
	Indices []int
}

type killTask struct {
	Type string
}

type controlNetTask struct {
	Type    string
	Timeout string
	Delay   string
}

type command struct {
	Tasks       []interface{}
	Deployments []deployment
}

func NewClient(baseURL string, operationTimeout time.Duration, pollingInterval time.Duration) Client {
	return Client{
		baseURL:          baseURL,
		operationTimeout: operationTimeout,
		pollingInterval:  pollingInterval,
	}
}

func (c Client) Delay(deploymentName string, jobName string, indices []int, delay time.Duration, timeout time.Duration) (Response, error) {
	command := command{
		Tasks: []interface{}{
			controlNetTask{Type: "control-net",
				Timeout: fmt.Sprintf("%dms", timeout.Nanoseconds()/million),
				Delay:   fmt.Sprintf("%dms", delay.Nanoseconds()/million)}},
		Deployments: []deployment{{
			Name: deploymentName,
			Jobs: []job{{Name: jobName, Indices: indices}},
		}},
	}

	jsonCommand, err := json.Marshal(command)
	if err != nil {
		return Response{}, err
	}

	resp, err := c.makeRequest("POST", c.baseURL+"/api/v1/incidents", bytes.NewBuffer(jsonCommand))
	if err != nil {
		return Response{}, err
	}

	return c.pollControlNetStarted(resp.ID)
}

func (c Client) pollControlNetStarted(id string) (Response, error) {
	startTime := time.Now()
	for {
		turbulenceResponse, err := c.makeRequest("GET", fmt.Sprintf("%s/api/v1/incidents/%s", c.baseURL, id), nil)
		if err != nil {
			return turbulenceResponse, err
		}

		for _, event := range turbulenceResponse.Events {
			if event.Error != "" {
				return turbulenceResponse, errors.New(event.Error)
			}
			if event.Type == "ControlNet" && event.ExecutionStartedAt != "" {
				return turbulenceResponse, nil
			}
		}

		if time.Now().Sub(startTime) > c.operationTimeout {
			return turbulenceResponse, errors.New(fmt.Sprintf("Did not start control-net event in time: %d", c.operationTimeout))
		}

		time.Sleep(c.pollingInterval)
	}
}

func (c Client) KillIndices(deploymentName, jobName string, indices []int) error {
	command := command{
		Tasks: []interface{}{
			killTask{Type: "kill"},
		},
		Deployments: []deployment{{
			Name: deploymentName,
			Jobs: []job{{Name: jobName, Indices: indices}},
		}},
	}

	jsonCommand, err := json.Marshal(command)
	if err != nil {
		return err
	}

	turbulenceResponse, err := c.makeRequest("POST", c.baseURL+"/api/v1/incidents", bytes.NewBuffer(jsonCommand))
	if err != nil {
		return err
	}

	return c.pollRequestCompletedDeletingVM(turbulenceResponse.ID)
}

func (c Client) pollRequestCompletedDeletingVM(id string) error {
	startTime := time.Now()
	for {
		turbulenceResponse, err := c.makeRequest("GET", fmt.Sprintf("%s/api/v1/incidents/%s", c.baseURL, id), nil)
		if err != nil {
			return err
		}

		if turbulenceResponse.ExecutionCompletedAt != "" {
			if len(turbulenceResponse.Events) == 0 {
				return errors.New("There should at least be one Event in response from turbulence.")
			}

			for _, event := range turbulenceResponse.Events {
				if event.Error != "" {
					return fmt.Errorf("There was a turbulence event error. Check out the turbulence events (response id: %s) for more information.", id)
				}
			}

			return nil
		}

		if time.Now().Sub(startTime) > c.operationTimeout {
			return errors.New(fmt.Sprintf("Did not finish deleting VM in time: %d", c.operationTimeout))
		}

		time.Sleep(c.pollingInterval)
	}
}

func (c Client) makeRequest(method string, path string, body io.Reader) (Response, error) {
	request, err := http.NewRequest(method, path, body)
	if err != nil {
		return Response{}, err
	}

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}

	resp, err := client.Do(request)
	if err != nil {
		return Response{}, err
	}

	var turbulenceResponse Response
	err = json.NewDecoder(resp.Body).Decode(&turbulenceResponse)
	if err != nil {
		return Response{}, errors.New("Unable to decode turbulence response.")
	}

	return turbulenceResponse, nil
}

func (c Client) Incident(id string) (Response, error) {
	return c.makeRequest("GET", fmt.Sprintf("%s/api/v1/incidents/%s", c.baseURL, id), nil)
}
<|endoftext|>