<commit_before>/*
 * Copyright 2016 Xuyuan Pang
 * Author: Xuyuan Pang
 *
 * 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 hodor

import (
	"net/http"
	"runtime"
	"time"
)

// Filter interface
type Filter interface {
	Do(http.Handler) http.Handler
}

// FilterFunc type
type FilterFunc func(next http.Handler) http.Handler

// Do calls FilterFunc
func (f FilterFunc) Do(next http.Handler) http.Handler {
	return f(next)
}

var emptyFilter FilterFunc = func(next http.Handler) http.Handler {
	return next
}

// MergeFilters merges multi fileers into a single one.
func MergeFilters(filters ...Filter) Filter {
	return FilterFunc(
		func(next http.Handler) http.Handler {
			for i := len(filters) - 1; i >= 0; i-- {
				next = filters[i].Do(next)
			}
			return next
		})
}

// Logger interface
type Logger interface {
	Printf(string, ...interface{})
}

// LogFilter new filter
func LogFilter(l Logger) FilterFunc {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(
			func(w http.ResponseWriter, r *http.Request) {
				addr := r.Header.Get("X-Real-IP")
				if addr == "" {
					addr = r.Header.Get("X-Forwarded-For")
					if addr == "" {
						addr = r.RemoteAddr
					}
				}
				path := r.URL.Path
				if r.URL.RawQuery != "" {
					path += "?" + r.URL.RawQuery
				}
				start := time.Now()
				l.Printf("%s %s %s", r.Method, path, addr)
				next.ServeHTTP(w, r)
				status := w.(ResponseWriter).Status()
				l.Printf("%d %s %s", status, http.StatusText(status), time.Since(start))
			})
	}
}

// RecoveryFilter new filter
func RecoveryFilter(l Logger) FilterFunc {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(
			func(w http.ResponseWriter, r *http.Request) {
				defer func() {
					if err := recover(); err != nil {
						trace := make([]byte, 1<<16)
						n := runtime.Stack(trace, true)
						stack := trace[:n]
						l.Printf("PANIC: %v\n%s", err, stack)
						http.Error(w, http.StatusText(http.StatusInternalServerError),
							http.StatusInternalServerError)
					}
				}()
				next.ServeHTTP(w, r)
			})
	}
}
<commit_msg>Added LogFunc<commit_after>/*
 * Copyright 2016 Xuyuan Pang
 * Author: Xuyuan Pang
 *
 * 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 hodor

import (
	"net/http"
	"runtime"
	"time"
)

// Filter interface
type Filter interface {
	Do(http.Handler) http.Handler
}

// FilterFunc type
type FilterFunc func(next http.Handler) http.Handler

// Do calls FilterFunc
func (f FilterFunc) Do(next http.Handler) http.Handler {
	return f(next)
}

var emptyFilter FilterFunc = func(next http.Handler) http.Handler {
	return next
}

// MergeFilters merges multi fileers into a single one.
func MergeFilters(filters ...Filter) Filter {
	return FilterFunc(
		func(next http.Handler) http.Handler {
			for i := len(filters) - 1; i >= 0; i-- {
				next = filters[i].Do(next)
			}
			return next
		})
}

// Logger interface
type Logger interface {
	Printf(string, ...interface{})
}

// LogFunc is a function implemented Logger interface
type LogFunc func(string, ...interface{})

// Printf func
func (f LogFunc) Printf(format string, args ...interface{}) {
	f(format, args...)
}

// LogFilter new filter
func LogFilter(l Logger) FilterFunc {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(
			func(w http.ResponseWriter, r *http.Request) {
				addr := r.Header.Get("X-Real-IP")
				if addr == "" {
					addr = r.Header.Get("X-Forwarded-For")
					if addr == "" {
						addr = r.RemoteAddr
					}
				}
				path := r.URL.Path
				if r.URL.RawQuery != "" {
					path += "?" + r.URL.RawQuery
				}
				start := time.Now()
				l.Printf("%s %s %s", r.Method, path, addr)
				next.ServeHTTP(w, r)
				status := w.(ResponseWriter).Status()
				l.Printf("%d %s %s", status, http.StatusText(status), time.Since(start))
			})
	}
}

// RecoveryFilter new filter
func RecoveryFilter(l Logger) FilterFunc {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(
			func(w http.ResponseWriter, r *http.Request) {
				defer func() {
					if err := recover(); err != nil {
						trace := make([]byte, 1<<16)
						n := runtime.Stack(trace, true)
						stack := trace[:n]
						l.Printf("PANIC: %v\n%s", err, stack)
						http.Error(w, http.StatusText(http.StatusInternalServerError),
							http.StatusInternalServerError)
					}
				}()
				next.ServeHTTP(w, r)
			})
	}
}
<|endoftext|>


<commit_before>package filter

import (
	"regexp"
	"strings"
)

type Filter func(string) string

var Filters = map[string]Filter{
	"uef.isolang":    dcLanguageIso,
	"uef.peerreview": eprintStatus,
	"uef.type":       eprintType,
	"uef.doi":        doi,
}

// try to transform strings into dc.language.iso fields
// using ISO 639-1 (two character codes)
// http://www.infoterm.info/standardization/iso_639_1_2002.php
func dcLanguageIso(s string) string {
	t := strings.ToLower(s)

	if t == "suomi" {
		return "FI"
	}

	if t == "ruotsi" {
		return "SV"
	}

	if t == "englanti" {
		return "EN"
	}

	if t == "eesti, viro" {
		return "ET"
	}

	if t == "portugali" {
		return "PT"
	}

	if t == "espanja" {
		return "ES"
	}

	if t == "venäjä" {
		return "RU"
	}

	return s
}

// Peer review status from input "0" or "1"
func eprintStatus(s string) string {
	if s == "0" {
		return "http://purl.org/eprint/status/NonPeerReviewed"
	} else if s == "1" {
		return "http://purl.org/eprint/status/PeerReviewed"
	}

	return s
}

// Try to map from SoleCRIS to ePrintTypes
func eprintType(s string) string {

	// Journal Article
	if s == "Ammatilliset aikakauslehtiartikkelit" ||
		s == "Muut aikakauslehtiartikkelit" ||
		s == "Tieteelliset aikakauslehtiartikkelit" {
		return "http://purl.org/eprint/type/JournalArticle"
	}

	// Book Item
	if s == "Artikkelit tieteellisissä kokoomateoksissa" ||
		s == "Artikkelit muissa kokoomateoksissa" {
		return "http://purl.org/eprint/type/BookItem"
	}

	// Book
	if s == "Ammatilliset kirjat" ||
		s == "Tieteelliset kirjat" ||
		s == "Toimitetut ammatilliset kirjat / lehden erikoisnumerot" ||
		s == "Toimitetut  tieteelliset kirjat / lehden erikoisnumerot" ||
		s == "Yleistajuiset kirjat" {
		return "http://purl.org/eprint/type/Book"
	}

	// Thesis
	if s == "Väitöskirjat" {
		return "http://purl.org/eprint/type/Thesis"
	}

	return s
}

// Try to convert doi's to http://doi.org/xxx -format
// This filter is woefully underspecified and specific to
// UEF SoleCRIS conventions.
// See i.e. for http://stackoverflow.com/questions/27910/finding-a-doi-in-a-document-or-page
// http://blog.crossref.org/2015/08/doi-regular-expressions.html
// for guidance in writing better logic
func doi(s string) string {

	lc := strings.ToLower(s) // use lower case for all but simple tests

	if s == "-" { // means no doi in UEF input convention
		return ""
	}

	if strings.HasPrefix(lc, "http://") { // if it's http, leave it as it is
		return s
	}

	if strings.HasPrefix(lc, "doi:") { // add http-prefix, trust that the rest is correct doi
		return "http://doi.org/" + s[4:]
	}

	// try doi-regexp (does work for majority of cases, but not for all of them
	if matched, _ := regexp.MatchString(`^10.\d{4,9}/[-._;()/:A-Za-z0-9]+$`, s); matched {
		return "http://doi.org/" + s
	}

	// otherwise don't do anything
	return s

}
<commit_msg>Added OpenAIRE type filter<commit_after>package filter

import (
	"regexp"
	"strings"
)

type Filter func(string) string

var Filters = map[string]Filter{
	"uef.isolang":       dcLanguageIso,
	"uef.peerreview":    eprintStatus,
	"uef.type":          eprintType,
	"uef.doi":           doi,
	"uef.openaire-type": openAireType,
}

// TODO: - provide generic comparison from tables
// TODO: - read comparison tables from files

// try to transform strings into dc.language.iso fields
// using ISO 639-1 (two character codes)
// http://www.infoterm.info/standardization/iso_639_1_2002.php
func dcLanguageIso(s string) string {
	t := strings.ToLower(s)

	if t == "suomi" {
		return "FI"
	}

	if t == "ruotsi" {
		return "SV"
	}

	if t == "englanti" {
		return "EN"
	}

	if t == "eesti, viro" {
		return "ET"
	}

	if t == "portugali" {
		return "PT"
	}

	if t == "espanja" {
		return "ES"
	}

	if t == "venäjä" {
		return "RU"
	}

	return s
}

// Peer review status from input "0" or "1"
func eprintStatus(s string) string {
	if s == "0" {
		return "http://purl.org/eprint/status/NonPeerReviewed"
	} else if s == "1" {
		return "http://purl.org/eprint/status/PeerReviewed"
	}

	return s
}

// Try to map from SoleCRIS to ePrintTypes
func eprintType(s string) string {

	// Journal Article
	if s == "Ammatilliset aikakauslehtiartikkelit" ||
		s == "Muut aikakauslehtiartikkelit" ||
		s == "Tieteelliset aikakauslehtiartikkelit" {
		return "http://purl.org/eprint/type/JournalArticle"
	}

	// Book Item
	if s == "Artikkelit tieteellisissä kokoomateoksissa" ||
		s == "Artikkelit muissa kokoomateoksissa" {
		return "http://purl.org/eprint/type/BookItem"
	}

	// Book
	if s == "Ammatilliset kirjat" ||
		s == "Tieteelliset kirjat" ||
		s == "Toimitetut ammatilliset kirjat / lehden erikoisnumerot" ||
		s == "Toimitetut  tieteelliset kirjat / lehden erikoisnumerot" ||
		s == "Yleistajuiset kirjat" {
		return "http://purl.org/eprint/type/Book"
	}

	// Thesis
	if s == "Väitöskirjat" {
		return "http://purl.org/eprint/type/Thesis"
	}

	return s
}

// Try to map from SoleCRIS to types used in OpenAIRE
// See: https://www.kiwi.fi/display/Julkaisuarkistopalvelut/OpenAiren+vaatimat+muutokset
func openAireType(s string) string {

	// Article
	if s == "Tieteelliset aikakauslehtiartikkelit" ||
		s == "Muut aikakauslehtiartikkelit" ||
		s == "Ammatilliset aikakauslehtiartikkelit" ||
		s == "Artikkelit tieteellisissä kokoomateoksissa" ||
		s == "Artikkelit muissa kokoomateoksissa" {
		return "article"
	}

	// Types of theses
	if s == "Väitöskirjat" {
		return "doctoralThesis"
	}

	if s == "Lisensiaatintutkimukset" {
		return "other" // No equivalent in OpenAIRE
	}

	if s == "Pro gradu -tutkielmat tai vastaavat" {
		return "masterThesis" // SoleCRIS might include bachelor's theses here?
	}

	// Book
	if s == "Ammatilliset kirjat" ||
		s == "Tieteelliset kirjat" ||
		s == "Toimitetut ammatilliset kirjat / lehden erikoisnumerot" ||
		s == "Toimitetut  tieteelliset kirjat / lehden erikoisnumerot" ||
		s == "Yleistajuiset kirjat" {
		return "book"
	}

	return s
}

// Try to convert doi's to http://doi.org/xxx -format
// This filter is woefully underspecified and specific to
// UEF SoleCRIS conventions.
// See i.e. for http://stackoverflow.com/questions/27910/finding-a-doi-in-a-document-or-page
// http://blog.crossref.org/2015/08/doi-regular-expressions.html
// for guidance in writing better logic
func doi(s string) string {

	lc := strings.ToLower(s) // use lower case for all but simple tests

	if s == "-" { // means no doi in UEF input convention
		return ""
	}

	if strings.HasPrefix(lc, "http://") { // if it's http, leave it as it is
		return s
	}

	if strings.HasPrefix(lc, "doi:") { // add http-prefix, trust that the rest is correct doi
		return "http://doi.org/" + s[4:]
	}

	// try doi-regexp (does work for majority of cases, but not for all of them
	if matched, _ := regexp.MatchString(`^10.\d{4,9}/[-._;()/:A-Za-z0-9]+$`, s); matched {
		return "http://doi.org/" + s
	}

	// otherwise don't do anything
	return s

}
<|endoftext|>


<commit_before>/*
Copyright (c) 2014 VMware, 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 govmomi

import (
	"github.com/vmware/govmomi/vim25/mo"
	"github.com/vmware/govmomi/vim25/types"
)

type Folder struct {
	types.ManagedObjectReference
}

func (f Folder) Reference() types.ManagedObjectReference {
	return f.ManagedObjectReference
}

func (f Folder) Children(c *Client) ([]Reference, error) {
	var mf mo.Folder

	err := c.Properties(f.Reference(), []string{"childType", "childEntity"}, &mf)
	if err != nil {
		return nil, err
	}

	var rs []Reference

	for _, e := range mf.ChildEntity {
		// A folder contains managed entities, all of which are listed below.
		switch e.Type {
		case "Folder":
			rs = append(rs, Folder{e})
		case "Datacenter":
			rs = append(rs, Datacenter{e})
		case "VirtualMachine":
			panic("TODO")
		case "VirtualApp":
			panic("TODO")
		case "ComputeResource":
			panic("TODO")
		case "Network":
			panic("TODO")
		case "Datastore":
			panic("TODO")
		default:
			panic("Unknown managed entity: " + e.Type)
		}
	}

	return rs, nil
}
<commit_msg>Retrieve only childEntity property for folder<commit_after>/*
Copyright (c) 2014 VMware, 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 govmomi

import (
	"github.com/vmware/govmomi/vim25/mo"
	"github.com/vmware/govmomi/vim25/types"
)

type Folder struct {
	types.ManagedObjectReference
}

func (f Folder) Reference() types.ManagedObjectReference {
	return f.ManagedObjectReference
}

func (f Folder) Children(c *Client) ([]Reference, error) {
	var mf mo.Folder

	err := c.Properties(f.Reference(), []string{"childEntity"}, &mf)
	if err != nil {
		return nil, err
	}

	var rs []Reference

	for _, e := range mf.ChildEntity {
		// A folder contains managed entities, all of which are listed below.
		switch e.Type {
		case "Folder":
			rs = append(rs, Folder{ManagedObjectReference: e})
		case "Datacenter":
			rs = append(rs, Datacenter{ManagedObjectReference: e})
		case "VirtualMachine":
			panic("TODO")
		case "VirtualApp":
			panic("TODO")
		case "ComputeResource":
			panic("TODO")
		case "Network":
			panic("TODO")
		case "Datastore":
			panic("TODO")
		default:
			panic("Unknown managed entity: " + e.Type)
		}
	}

	return rs, nil
}
<|endoftext|>


<commit_before>package lsh

import (
	"fmt"
	"sync"
)

type treeNode struct {
	// Hash key for this intermediate node. nil/empty for root nodes.
	hashKey int
	// A list of indices to the source dataset.
	indices []int
	// Child nodes, keyed by partial hash value.
	children map[int]*treeNode
}

// recursiveAdd recurses down the tree to find the correct location to insert id.
// Returns whether a new hash value was added.
func (node *treeNode) recursiveAdd(level int, id int, tableKey hashTableKey) bool {
	if level == len(tableKey) {
		node.indices = append(node.indices, id)
		return false
	} else {
		// Check if next hash exists in children map. If not, create.
		var next *treeNode
		hasNewHash := false
		if nextNode, ok := node.children[tableKey[level]]; !ok {
			next = &treeNode{
				hashKey:  tableKey[level],
				indices:  make([]int, 0),
				children: make(map[int]*treeNode),
			}
			node.children[tableKey[level]] = next
			hasNewHash = true
		} else {
			next = nextNode
		}
		// Recurse using next level's hash value.
		recursive := next.recursiveAdd(level+1, id, tableKey)
		return hasNewHash || recursive
	}
}

func tab(times int) {
	for i := 0; i < times; i++ {
		fmt.Print("    ")
	}
}

func (node *treeNode) dump(level int) {
	tab(level)
	fmt.Printf("{ (%d): indices %o ", node.hashKey, node.indices)
	if len(node.children) > 0 {
		fmt.Printf("[\n")
		for _, v := range node.children {
			v.dump(level + 1)
		}
		tab(level)
		fmt.Print("] }\n")
	} else {
		fmt.Print("}\n")
	}
}

type prefixTree struct {
	// Number of distinct elements in the tree.
	count int
	// Pointer to the root node.
	root *treeNode
}

func (tree *prefixTree) insertIntoTree(id int, tableKey hashTableKey) {
	if tree.root.recursiveAdd(0, id, tableKey) {
		tree.count++
	}
}

func (tree *prefixTree) lookup(maxLevel int, tableKey hashTableKey) []int {
	indices := make([]int, 0)
	currentNode := tree.root
	// fmt.Println(tableKey)
	for level := 0; level < len(tableKey) && level < maxLevel; level++ {
		if next, ok := currentNode.children[tableKey[level]]; !ok {
			return indices
		} else {
			currentNode = next
			// fmt.Printf("Found hash key %d at level %d, current hash %d\n", tableKey[level], level, currentNode.hashKey)
		}
	}

	// Grab all indices of nodes descendent from the current node.
	queue := []*treeNode{currentNode}
	for len(queue) > 0 {
		// Add node's indices to main list.
		indices = append(indices, queue[0].indices...)

		// Add children.
		for _, child := range queue[0].children {
			queue = append(queue, child)
		}

		// Done with head.
		queue = queue[1:]
	}
	// fmt.Printf("Result: %o\n", indices)
	return indices
}

// LshForest implements the LSH Forest algorithm by Mayank Bawa et.al.
// It supports both nearest neighbour candidate query and k-NN query.
type LshForest struct {
	// Embedded type
	*lshParams
	// Trees.
	trees []prefixTree
}

// NewLshForest creates a new LSH Forest for L2 distance.
// dim is the diminsionality of the data, l is the number of hash
// tables to use, m is the number of hash values to concatenate to
// form the key to the hash tables, w is the slot size for the
// family of LSH functions.
func NewLshForest(dim, l, m int, w float64) *LshForest {
	trees := make([]prefixTree, l)
	for i, _ := range trees {
		trees[i].count = 0
		trees[i].root = &treeNode{
			hashKey:  0,
			indices:  make([]int, 0),
			children: make(map[int]*treeNode),
		}
	}
	return &LshForest{
		lshParams: newLshParams(dim, l, m, w),
		trees:     trees,
	}
}

// Insert adds a new data point to the LSH Forest.
// id is the unique identifier for the data point.
func (index *LshForest) Insert(point Point, id int) {
	// Apply hash functions.
	hvs := index.hash(point)
	// Parallel insert
	var wg sync.WaitGroup
	for i := range index.trees {
		hv := hvs[i]
		tree := &(index.trees[i])
		wg.Add(1)
		go func(tree *prefixTree, hv hashTableKey) {
			tree.insertIntoTree(id, hv)
			wg.Done()
		}(tree, hv)
	}
	wg.Wait()
}

// Helper that queries all trees and returns an array of distinct indices.
func (index *LshForest) queryHelper(maxLevel int, tableKeys []hashTableKey) []int {
	// Keep track of keys seen
	indices := make([]int, 0)
	seens := make(map[int]bool)
	for i, tree := range index.trees {
		for _, candidate := range tree.lookup(maxLevel, tableKeys[i]) {
			if _, seen := seens[candidate]; !seen {
				seens[candidate] = true
				indices = append(indices, candidate)
			}
		}
	}
	return indices
}

// Query returns the ids of approximate nearest neighbour candidates,
// in un-sorted order, given the query point,
// and writes them to an output channel, out.
// Note: the function does not close the channel.
func (index *LshForest) Query(q Point, out chan int) {
	// Apply hash functions
	hvs := index.hash(q)
	for _, candidate := range index.queryHelper(index.m, hvs) {
		out <- candidate
	}
}

// QueryKnn returns the ids of the top-k approximate nearest neighbours,
// in un-sorted order, given the query point,
// and writes them to an output channel, out.
// Note: the function does not close the channel.
func (index *LshForest) QueryKnn(q Point, k int, out chan int) {
	// Apply hash functions
	hvs := index.hash(q)
	candidates := make([]int, 0)
	for maxLevels := index.m; maxLevels >= 0; maxLevels-- {
		candidates = index.queryHelper(maxLevels, hvs)
		// Enough candidates at this level, so we can rank and return.
		if len(candidates) >= k {
			break
		}
	}
	for _, candidate := range candidates {
		out <- candidate
	}
}

// Dump prints out the index for debugging
func (index *LshForest) dump() {
	for i, tree := range index.trees {
		fmt.Printf("Tree %d (%d hash values):\n", i, tree.count)
		tree.root.dump(0)
	}
}
<commit_msg>Fix memory leak<commit_after>package lsh

import (
	"fmt"
	"sync"
)

type treeNode struct {
	// Hash key for this intermediate node. nil/empty for root nodes.
	hashKey int
	// A list of indices to the source dataset.
	indices []int
	// Child nodes, keyed by partial hash value.
	children map[int]*treeNode
}

func (node *treeNode) recursiveDelete() {
	for _, child := range node.children {
		if len((child).children) > 0 {
			(child).recursiveDelete()
		}

		if len(child.indices) > 0 {
			node.indices = nil
		}
	}

	node.indices = nil
	node.children = nil
}

// recursiveAdd recurses down the tree to find the correct location to insert id.
// Returns whether a new hash value was added.
func (node *treeNode) recursiveAdd(level int, id int, tableKey hashTableKey) bool {
	if level == len(tableKey) {
		node.indices = append(node.indices, id)
		return false
	} else {
		// Check if next hash exists in children map. If not, create.
		var next *treeNode
		hasNewHash := false
		if nextNode, ok := node.children[tableKey[level]]; !ok {
			next = &treeNode{
				hashKey:  tableKey[level],
				indices:  make([]int, 0),
				children: make(map[int]*treeNode),
			}
			node.children[tableKey[level]] = next
			hasNewHash = true
		} else {
			next = nextNode
		}
		// Recurse using next level's hash value.
		recursive := next.recursiveAdd(level+1, id, tableKey)
		return hasNewHash || recursive
	}
}

func tab(times int) {
	for i := 0; i < times; i++ {
		fmt.Print("    ")
	}
}

func (node *treeNode) dump(level int) {
	tab(level)
	fmt.Printf("{ (%d): indices %o ", node.hashKey, node.indices)
	if len(node.children) > 0 {
		fmt.Printf("[\n")
		for _, v := range node.children {
			v.dump(level + 1)
		}
		tab(level)
		fmt.Print("] }\n")
	} else {
		fmt.Print("}\n")
	}
}

type prefixTree struct {
	// Number of distinct elements in the tree.
	count int
	// Pointer to the root node.
	root *treeNode
}

func (tree *prefixTree) insertIntoTree(id int, tableKey hashTableKey) {
	if tree.root.recursiveAdd(0, id, tableKey) {
		tree.count++
	}
}

func (tree *prefixTree) lookup(maxLevel int, tableKey hashTableKey) []int {
	indices := make([]int, 0)
	currentNode := tree.root
	// fmt.Println(tableKey)
	for level := 0; level < len(tableKey) && level < maxLevel; level++ {
		if next, ok := currentNode.children[tableKey[level]]; !ok {
			return indices
		} else {
			currentNode = next
			// fmt.Printf("Found hash key %d at level %d, current hash %d\n", tableKey[level], level, currentNode.hashKey)
		}
	}

	// Grab all indices of nodes descendent from the current node.
	queue := []*treeNode{currentNode}
	for len(queue) > 0 {
		// Add node's indices to main list.
		indices = append(indices, queue[0].indices...)

		// Add children.
		for _, child := range queue[0].children {
			queue = append(queue, child)
		}

		// Done with head.
		queue = queue[1:]
	}
	// fmt.Printf("Result: %o\n", indices)
	return indices
}

// LshForest implements the LSH Forest algorithm by Mayank Bawa et.al.
// It supports both nearest neighbour candidate query and k-NN query.
type LshForest struct {
	// Embedded type
	*lshParams
	// Trees.
	trees []prefixTree
}

// NewLshForest creates a new LSH Forest for L2 distance.
// dim is the diminsionality of the data, l is the number of hash
// tables to use, m is the number of hash values to concatenate to
// form the key to the hash tables, w is the slot size for the
// family of LSH functions.
func NewLshForest(dim, l, m int, w float64) *LshForest {
	trees := make([]prefixTree, l)
	for i, _ := range trees {
		trees[i].count = 0
		trees[i].root = &treeNode{
			hashKey:  0,
			indices:  make([]int, 0),
			children: make(map[int]*treeNode),
		}
	}
	return &LshForest{
		lshParams: newLshParams(dim, l, m, w),
		trees:     trees,
	}
}

func (index *LshForest) Delete() {
	for _, tree := range index.trees {
		(*tree.root).recursiveDelete()
	}
}

// Insert adds a new data point to the LSH Forest.
// id is the unique identifier for the data point.
func (index *LshForest) Insert(point Point, id int) {
	// Apply hash functions.
	hvs := index.hash(point)
	// Parallel insert
	var wg sync.WaitGroup
	for i := range index.trees {
		hv := hvs[i]
		tree := &(index.trees[i])
		wg.Add(1)
		go func(tree *prefixTree, hv hashTableKey) {
			tree.insertIntoTree(id, hv)
			wg.Done()
		}(tree, hv)
	}
	wg.Wait()
}

// Helper that queries all trees and returns an array of distinct indices.
func (index *LshForest) queryHelper(maxLevel int, tableKeys []hashTableKey) []int {
	// Keep track of keys seen
	indices := make([]int, 0)
	seens := make(map[int]bool)
	for i, tree := range index.trees {
		for _, candidate := range tree.lookup(maxLevel, tableKeys[i]) {
			if _, seen := seens[candidate]; !seen {
				seens[candidate] = true
				indices = append(indices, candidate)
			}
		}
	}
	return indices
}

// Query returns the ids of approximate nearest neighbour candidates,
// in un-sorted order, given the query point,
// and writes them to an output channel, out.
// Note: the function does not close the channel.
func (index *LshForest) Query(q Point, out chan int) {
	// Apply hash functions
	hvs := index.hash(q)
	for _, candidate := range index.queryHelper(index.m, hvs) {
		out <- candidate
	}
}

// QueryKnn returns the ids of the top-k approximate nearest neighbours,
// in un-sorted order, given the query point,
// and writes them to an output channel, out.
// Note: the function does not close the channel.
func (index *LshForest) QueryKnn(q Point, k int, out chan int) {
	// Apply hash functions
	hvs := index.hash(q)
	candidates := make([]int, 0)
	for maxLevels := index.m; maxLevels >= 0; maxLevels-- {
		candidates = index.queryHelper(maxLevels, hvs)
		// Enough candidates at this level, so we can rank and return.
		if len(candidates) >= k {
			break
		}
	}
	for _, candidate := range candidates {
		out <- candidate
	}
}

// Dump prints out the index for debugging
func (index *LshForest) dump() {
	for i, tree := range index.trees {
		fmt.Printf("Tree %d (%d hash values):\n", i, tree.count)
		tree.root.dump(0)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) Liam Stanley <me@liamstanley.io>. All rights reserved. Use
// of this source code is governed by the MIT license that can be found in
// the LICENSE file.

package girc

import (
	"bytes"
	"fmt"
	"regexp"
	"strings"
)

const (
	fmtOpenChar  = '{'
	fmtCloseChar = '}'
)

var fmtColors = map[string]int{
	"white":       0,
	"black":       1,
	"blue":        2,
	"navy":        2,
	"green":       3,
	"red":         4,
	"brown":       5,
	"maroon":      5,
	"purple":      6,
	"gold":        7,
	"olive":       7,
	"orange":      7,
	"yellow":      8,
	"lightgreen":  9,
	"lime":        9,
	"teal":        10,
	"cyan":        11,
	"lightblue":   12,
	"royal":       12,
	"fuchsia":     13,
	"lightpurple": 13,
	"pink":        13,
	"gray":        14,
	"grey":        14,
	"lightgrey":   15,
	"silver":      15,
}

var fmtCodes = map[string]string{
	"bold":      "\x02",
	"b":         "\x02",
	"italic":    "\x1d",
	"i":         "\x1d",
	"reset":     "\x0f",
	"r":         "\x0f",
	"clear":     "\x03",
	"c":         "\x03", // Clears formatting.
	"reverse":   "\x16",
	"underline": "\x1f",
	"ul":        "\x1f",
	"ctcp":      "\x01", // CTCP/ACTION delimiter.
}

// Fmt takes format strings like "{red}" or "{red,blue}" (for background
// colors) and turns them into the resulting ASCII format/color codes for IRC.
// See format.go for the list of supported format codes allowed.
//
// For example:
//
//   client.Message("#channel", Fmt("{red}{b}Hello {red,blue}World{c}"))
func Fmt(text string) string {
	var last = -1
	for i := 0; i < len(text); i++ {
		if text[i] == fmtOpenChar {
			last = i
			continue
		}

		if text[i] == fmtCloseChar && last > -1 {
			code := strings.ToLower(text[last+1 : i])

			// Check to see if they're passing in a second (background) color
			// as {fgcolor,bgcolor}.
			var secondary string
			if com := strings.Index(code, ","); com > -1 {
				secondary = code[com+1:]
				code = code[:com]
			}

			var repl string

			if color, ok := fmtColors[code]; ok {
				repl = fmt.Sprintf("\x03%02d", color)
			}

			if repl != "" && secondary != "" {
				if color, ok := fmtColors[secondary]; ok {
					repl += fmt.Sprintf(",%02d", color)
				}
			}

			if repl == "" {
				if fmtCode, ok := fmtCodes[code]; ok {
					repl = fmtCode
				}
			}

			next := len(text[:last]+repl) - 1
			text = text[:last] + repl + text[i+1:]
			last = -1
			i = next
			continue
		}

		if last > -1 {
			// A-Z, a-z, and ","
			if text[i] != ',' && (text[i] <= 'A' || text[i] >= 'Z') && (text[i] <= 'a' || text[i] >= 'z') {
				last = -1
				continue
			}
		}
	}

	return text
}

// TrimFmt strips all "{fmt}" formatting strings from the input text.
// See Fmt() for more information.
func TrimFmt(text string) string {
	for color := range fmtColors {
		text = strings.Replace(text, string(fmtOpenChar)+color+string(fmtCloseChar), "", -1)
	}
	for code := range fmtCodes {
		text = strings.Replace(text, string(fmtOpenChar)+code+string(fmtCloseChar), "", -1)
	}

	return text
}

// This is really the only fastest way of doing this (marginably better than
// actually trying to parse it manually.)
var reStripColor = regexp.MustCompile(`\x03([019]?[0-9](,[019]?[0-9])?)?`)

// StripRaw tries to strip all ASCII format codes that are used for IRC.
// Primarily, foreground/background colors, and other control bytes like
// reset, bold, italic, reverse, etc. This also is done in a specific way
// in order to ensure no truncation of other non-irc formatting.
func StripRaw(text string) string {
	text = reStripColor.ReplaceAllString(text, "")

	for _, code := range fmtCodes {
		text = strings.Replace(text, code, "", -1)
	}

	return text
}

// IsValidChannel validates if channel is an RFC complaint channel or not.
//
// NOTE: If you are using this to validate a channel that contains a channel
// ID, (!<channelid>NAME), this only supports the standard 5 character length.
//
// NOTE: If you do not need to validate against servers that support unicode,
// you may want to ensure that all channel chars are within the range of
// all ASCII printable chars. This function will NOT do that for
// compatibility reasons.
//
//   channel    =  ( "#" / "+" / ( "!" channelid ) / "&" ) chanstring
//                 [ ":" chanstring ]
//   chanstring =  0x01-0x07 / 0x08-0x09 / 0x0B-0x0C / 0x0E-0x1F / 0x21-0x2B
//   chanstring =  / 0x2D-0x39 / 0x3B-0xFF
//                   ; any octet except NUL, BELL, CR, LF, " ", "," and ":"
//   channelid  = 5( 0x41-0x5A / digit )   ; 5( A-Z / 0-9 )
func IsValidChannel(channel string) bool {
	if len(channel) <= 1 || len(channel) > 50 {
		return false
	}

	// #, +, !<channelid>, or &
	// Including "*" in the prefix list, as this is commonly used (e.g. ZNC)
	if bytes.IndexByte([]byte{'!', '#', '&', '*', '+'}, channel[0]) == -1 {
		return false
	}

	// !<channelid> -- not very commonly supported, but we'll check it anyway.
	// The ID must be 5 chars. This means min-channel size should be:
	//   1 (prefix) + 5 (id) + 1 (+, channel name)
	// On some networks, this may be extended with ISUPPORT capabilities,
	// however this is extremely uncommon.
	if channel[0] == '!' {
		if len(channel) < 7 {
			return false
		}

		// check for valid ID
		for i := 1; i < 6; i++ {
			if (channel[i] < '0' || channel[i] > '9') && (channel[i] < 'A' || channel[i] > 'Z') {
				return false
			}
		}
	}

	// Check for invalid octets here.
	bad := []byte{0x00, 0x07, 0x0D, 0x0A, 0x20, 0x2C, 0x3A}
	for i := 1; i < len(channel); i++ {
		if bytes.IndexByte(bad, channel[i]) != -1 {
			return false
		}
	}

	return true
}

// IsValidNick validates an IRC nickame. Note that this does not validate
// IRC nickname length.
//
//   nickname =  ( letter / special ) *8( letter / digit / special / "-" )
//   letter   =  0x41-0x5A / 0x61-0x7A
//   digit    =  0x30-0x39
//   special  =  0x5B-0x60 / 0x7B-0x7D
func IsValidNick(nick string) bool {
	if len(nick) <= 0 {
		return false
	}

	// Check the first index. Some characters aren't allowed for the first
	// index of an IRC nickname.
	if nick[0] < 'A' || nick[0] > '}' {
		// a-z, A-Z, and _\[]{}^|
		return false
	}

	for i := 1; i < len(nick); i++ {
		if (nick[i] < 'A' || nick[i] > '}') && (nick[i] < '0' || nick[i] > '9') && nick[i] != '-' {
			// a-z, A-Z, 0-9, -, and _\[]{}^|
			return false
		}
	}

	return true
}

// IsValidUser validates an IRC ident/username. Note that this does not
// validate IRC ident length.
//
// The validation checks are much like what characters are allowed with an
// IRC nickname (see IsValidNick()), however an ident/username can:
//
// 1. Must either start with alphanumberic char, or "~" then alphanumberic
// char.
//
// 2. Contain a "." (period), for use with "first.last". Though, this may
// not be supported on all networks. Some limit this to only a single period.
//
// Per RFC:
//   user =  1*( %x01-09 / %x0B-0C / %x0E-1F / %x21-3F / %x41-FF )
//           ; any octet except NUL, CR, LF, " " and "@"
func IsValidUser(name string) bool {
	if len(name) <= 0 {
		return false
	}

	// "~" is prepended (commonly) if there was no ident server response.
	if name[0] == '~' {
		// Means name only contained "~".
		if len(name) < 2 {
			return false
		}

		name = name[1:]
	}

	// Check to see if the first index is alphanumeric.
	if (name[0] < 'A' || name[0] > 'J') && (name[0] < 'a' || name[0] > 'z') && (name[0] < '0' || name[0] > '9') {
		return false
	}

	for i := 1; i < len(name); i++ {
		if (name[i] < 'A' || name[i] > '}') && (name[i] < '0' || name[i] > '9') && name[i] != '-' && name[i] != '.' {
			// a-z, A-Z, 0-9, -, and _\[]{}^|
			return false
		}
	}

	return true
}

// ToRFC1459 converts a string to the stripped down conversion within RFC
// 1459. This will do things like replace an "A" with an "a", "[]" with "{}",
// and so forth. Useful to compare two nicknames or channels. Note that this
// should not be used to normalize nicknames or similar, as this may convert
// valid input characters to non-rfc-valid characters. As such, it's main use
// is for comparing two nicks.
func ToRFC1459(input string) string {
	var out string

	for i := 0; i < len(input); i++ {
		if input[i] >= 65 && input[i] <= 94 {
			out += string(rune(input[i]) + 32)
		} else {
			out += string(input[i])
		}
	}

	return out
}

const globChar = "*"

// Glob will test a string pattern, potentially containing globs, against a
// string. The glob character is *.
func Glob(input, match string) bool {
	// Empty pattern.
	if match == "" {
		return input == match
	}

	// If a glob, match all.
	if match == globChar {
		return true
	}

	parts := strings.Split(match, globChar)

	if len(parts) == 1 {
		// No globs, test for equality.
		return input == match
	}

	leadingGlob, trailingGlob := strings.HasPrefix(match, globChar), strings.HasSuffix(match, globChar)
	last := len(parts) - 1

	// Check prefix first.
	if !leadingGlob && !strings.HasPrefix(input, parts[0]) {
		return false
	}

	// Check middle section.
	for i := 1; i < last; i++ {
		if !strings.Contains(input, parts[i]) {
			return false
		}

		// Trim already-evaluated text from input during loop over match
		// text.
		idx := strings.Index(input, parts[i]) + len(parts[i])
		input = input[idx:]
	}

	// Check suffix last.
	return trailingGlob || strings.HasSuffix(input, parts[last])
}
<commit_msg>add support for znc ~ channel prefixes, and ? user prefixes<commit_after>// Copyright (c) Liam Stanley <me@liamstanley.io>. All rights reserved. Use
// of this source code is governed by the MIT license that can be found in
// the LICENSE file.

package girc

import (
	"bytes"
	"fmt"
	"regexp"
	"strings"
)

const (
	fmtOpenChar  = '{'
	fmtCloseChar = '}'
)

var fmtColors = map[string]int{
	"white":       0,
	"black":       1,
	"blue":        2,
	"navy":        2,
	"green":       3,
	"red":         4,
	"brown":       5,
	"maroon":      5,
	"purple":      6,
	"gold":        7,
	"olive":       7,
	"orange":      7,
	"yellow":      8,
	"lightgreen":  9,
	"lime":        9,
	"teal":        10,
	"cyan":        11,
	"lightblue":   12,
	"royal":       12,
	"fuchsia":     13,
	"lightpurple": 13,
	"pink":        13,
	"gray":        14,
	"grey":        14,
	"lightgrey":   15,
	"silver":      15,
}

var fmtCodes = map[string]string{
	"bold":      "\x02",
	"b":         "\x02",
	"italic":    "\x1d",
	"i":         "\x1d",
	"reset":     "\x0f",
	"r":         "\x0f",
	"clear":     "\x03",
	"c":         "\x03", // Clears formatting.
	"reverse":   "\x16",
	"underline": "\x1f",
	"ul":        "\x1f",
	"ctcp":      "\x01", // CTCP/ACTION delimiter.
}

// Fmt takes format strings like "{red}" or "{red,blue}" (for background
// colors) and turns them into the resulting ASCII format/color codes for IRC.
// See format.go for the list of supported format codes allowed.
//
// For example:
//
//   client.Message("#channel", Fmt("{red}{b}Hello {red,blue}World{c}"))
func Fmt(text string) string {
	var last = -1
	for i := 0; i < len(text); i++ {
		if text[i] == fmtOpenChar {
			last = i
			continue
		}

		if text[i] == fmtCloseChar && last > -1 {
			code := strings.ToLower(text[last+1 : i])

			// Check to see if they're passing in a second (background) color
			// as {fgcolor,bgcolor}.
			var secondary string
			if com := strings.Index(code, ","); com > -1 {
				secondary = code[com+1:]
				code = code[:com]
			}

			var repl string

			if color, ok := fmtColors[code]; ok {
				repl = fmt.Sprintf("\x03%02d", color)
			}

			if repl != "" && secondary != "" {
				if color, ok := fmtColors[secondary]; ok {
					repl += fmt.Sprintf(",%02d", color)
				}
			}

			if repl == "" {
				if fmtCode, ok := fmtCodes[code]; ok {
					repl = fmtCode
				}
			}

			next := len(text[:last]+repl) - 1
			text = text[:last] + repl + text[i+1:]
			last = -1
			i = next
			continue
		}

		if last > -1 {
			// A-Z, a-z, and ","
			if text[i] != ',' && (text[i] <= 'A' || text[i] >= 'Z') && (text[i] <= 'a' || text[i] >= 'z') {
				last = -1
				continue
			}
		}
	}

	return text
}

// TrimFmt strips all "{fmt}" formatting strings from the input text.
// See Fmt() for more information.
func TrimFmt(text string) string {
	for color := range fmtColors {
		text = strings.Replace(text, string(fmtOpenChar)+color+string(fmtCloseChar), "", -1)
	}
	for code := range fmtCodes {
		text = strings.Replace(text, string(fmtOpenChar)+code+string(fmtCloseChar), "", -1)
	}

	return text
}

// This is really the only fastest way of doing this (marginably better than
// actually trying to parse it manually.)
var reStripColor = regexp.MustCompile(`\x03([019]?[0-9](,[019]?[0-9])?)?`)

// StripRaw tries to strip all ASCII format codes that are used for IRC.
// Primarily, foreground/background colors, and other control bytes like
// reset, bold, italic, reverse, etc. This also is done in a specific way
// in order to ensure no truncation of other non-irc formatting.
func StripRaw(text string) string {
	text = reStripColor.ReplaceAllString(text, "")

	for _, code := range fmtCodes {
		text = strings.Replace(text, code, "", -1)
	}

	return text
}

// IsValidChannel validates if channel is an RFC complaint channel or not.
//
// NOTE: If you are using this to validate a channel that contains a channel
// ID, (!<channelid>NAME), this only supports the standard 5 character length.
//
// NOTE: If you do not need to validate against servers that support unicode,
// you may want to ensure that all channel chars are within the range of
// all ASCII printable chars. This function will NOT do that for
// compatibility reasons.
//
//   channel    =  ( "#" / "+" / ( "!" channelid ) / "&" ) chanstring
//                 [ ":" chanstring ]
//   chanstring =  0x01-0x07 / 0x08-0x09 / 0x0B-0x0C / 0x0E-0x1F / 0x21-0x2B
//   chanstring =  / 0x2D-0x39 / 0x3B-0xFF
//                   ; any octet except NUL, BELL, CR, LF, " ", "," and ":"
//   channelid  = 5( 0x41-0x5A / digit )   ; 5( A-Z / 0-9 )
func IsValidChannel(channel string) bool {
	if len(channel) <= 1 || len(channel) > 50 {
		return false
	}

	// #, +, !<channelid>, ~, or &
	// Including "*" and "~" in the prefix list, as these are commonly used
	// (e.g. ZNC.)
	if bytes.IndexByte([]byte{'!', '#', '&', '*', '~', '+'}, channel[0]) == -1 {
		return false
	}

	// !<channelid> -- not very commonly supported, but we'll check it anyway.
	// The ID must be 5 chars. This means min-channel size should be:
	//   1 (prefix) + 5 (id) + 1 (+, channel name)
	// On some networks, this may be extended with ISUPPORT capabilities,
	// however this is extremely uncommon.
	if channel[0] == '!' {
		if len(channel) < 7 {
			return false
		}

		// check for valid ID
		for i := 1; i < 6; i++ {
			if (channel[i] < '0' || channel[i] > '9') && (channel[i] < 'A' || channel[i] > 'Z') {
				return false
			}
		}
	}

	// Check for invalid octets here.
	bad := []byte{0x00, 0x07, 0x0D, 0x0A, 0x20, 0x2C, 0x3A}
	for i := 1; i < len(channel); i++ {
		if bytes.IndexByte(bad, channel[i]) != -1 {
			return false
		}
	}

	return true
}

// IsValidNick validates an IRC nickame. Note that this does not validate
// IRC nickname length.
//
//   nickname =  ( letter / special ) *8( letter / digit / special / "-" )
//   letter   =  0x41-0x5A / 0x61-0x7A
//   digit    =  0x30-0x39
//   special  =  0x5B-0x60 / 0x7B-0x7D
func IsValidNick(nick string) bool {
	if len(nick) <= 0 {
		return false
	}

	// Check the first index. Some characters aren't allowed for the first
	// index of an IRC nickname.
	if (nick[0] < 'A' || nick[0] > '}') && nick[0] != '?' {
		// a-z, A-Z, '_\[]{}^|', and '?' in the case of znc.
		return false
	}

	for i := 1; i < len(nick); i++ {
		if (nick[i] < 'A' || nick[i] > '}') && (nick[i] < '0' || nick[i] > '9') && nick[i] != '-' {
			// a-z, A-Z, 0-9, -, and _\[]{}^|
			return false
		}
	}

	return true
}

// IsValidUser validates an IRC ident/username. Note that this does not
// validate IRC ident length.
//
// The validation checks are much like what characters are allowed with an
// IRC nickname (see IsValidNick()), however an ident/username can:
//
// 1. Must either start with alphanumberic char, or "~" then alphanumberic
// char.
//
// 2. Contain a "." (period), for use with "first.last". Though, this may
// not be supported on all networks. Some limit this to only a single period.
//
// Per RFC:
//   user =  1*( %x01-09 / %x0B-0C / %x0E-1F / %x21-3F / %x41-FF )
//           ; any octet except NUL, CR, LF, " " and "@"
func IsValidUser(name string) bool {
	if len(name) <= 0 {
		return false
	}

	// "~" is prepended (commonly) if there was no ident server response.
	if name[0] == '~' {
		// Means name only contained "~".
		if len(name) < 2 {
			return false
		}

		name = name[1:]
	}

	// Check to see if the first index is alphanumeric.
	if (name[0] < 'A' || name[0] > 'J') && (name[0] < 'a' || name[0] > 'z') && (name[0] < '0' || name[0] > '9') {
		return false
	}

	for i := 1; i < len(name); i++ {
		if (name[i] < 'A' || name[i] > '}') && (name[i] < '0' || name[i] > '9') && name[i] != '-' && name[i] != '.' {
			// a-z, A-Z, 0-9, -, and _\[]{}^|
			return false
		}
	}

	return true
}

// ToRFC1459 converts a string to the stripped down conversion within RFC
// 1459. This will do things like replace an "A" with an "a", "[]" with "{}",
// and so forth. Useful to compare two nicknames or channels. Note that this
// should not be used to normalize nicknames or similar, as this may convert
// valid input characters to non-rfc-valid characters. As such, it's main use
// is for comparing two nicks.
func ToRFC1459(input string) string {
	var out string

	for i := 0; i < len(input); i++ {
		if input[i] >= 65 && input[i] <= 94 {
			out += string(rune(input[i]) + 32)
		} else {
			out += string(input[i])
		}
	}

	return out
}

const globChar = "*"

// Glob will test a string pattern, potentially containing globs, against a
// string. The glob character is *.
func Glob(input, match string) bool {
	// Empty pattern.
	if match == "" {
		return input == match
	}

	// If a glob, match all.
	if match == globChar {
		return true
	}

	parts := strings.Split(match, globChar)

	if len(parts) == 1 {
		// No globs, test for equality.
		return input == match
	}

	leadingGlob, trailingGlob := strings.HasPrefix(match, globChar), strings.HasSuffix(match, globChar)
	last := len(parts) - 1

	// Check prefix first.
	if !leadingGlob && !strings.HasPrefix(input, parts[0]) {
		return false
	}

	// Check middle section.
	for i := 1; i < last; i++ {
		if !strings.Contains(input, parts[i]) {
			return false
		}

		// Trim already-evaluated text from input during loop over match
		// text.
		idx := strings.Index(input, parts[i]) + len(parts[i])
		input = input[idx:]
	}

	// Check suffix last.
	return trailingGlob || strings.HasSuffix(input, parts[last])
}
<|endoftext|>


<commit_before>/*
http://www.apache.org/licenses/LICENSE-2.0.txt

Copyright 2015 Intel Corporation

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 riemann

import (
	"bytes"
	"encoding/gob"
	"errors"
	"fmt"

	log "github.com/Sirupsen/logrus"
	"github.com/amir/raidman"

	"github.com/intelsdi-x/snap/control/plugin"
	"github.com/intelsdi-x/snap/control/plugin/cpolicy"
	"github.com/intelsdi-x/snap/core"
	"github.com/intelsdi-x/snap/core/ctypes"
)

const (
	PluginName    = "riemann"
	PluginVersion = 9
	PluginType    = plugin.PublisherPluginType
)

// Meta returns the metadata details for the Riemann Publisher Plugin
func Meta() *plugin.PluginMeta {
	return plugin.NewPluginMeta(PluginName, PluginVersion, PluginType, []string{plugin.SnapGOBContentType}, []string{plugin.SnapGOBContentType})
}

type riemannPublisher struct{}

// NewRiemannPublisher does something cool
func NewRiemannPublisher() *riemannPublisher {
	return &riemannPublisher{}
}

// GetConfigPolicy returns the config policy for the Riemann Publisher Plugin
func (r *riemannPublisher) GetConfigPolicy() (*cpolicy.ConfigPolicy, error) {
	cp := cpolicy.New()
	config := cpolicy.NewPolicyNode()

	// Riemann server to publish event to
	r1, err := cpolicy.NewStringRule("broker", true)
	handleErr(err)
	r1.Description = "Broker in the format of broker-ip:port (ex: 192.168.1.1:5555)"

	config.Add(r1)
	cp.Add([]string{""}, config)
	return cp, nil
}

// Publish serializes the data and calls publish to send events to Riemann
func (r *riemannPublisher) Publish(contentType string, content []byte, config map[string]ctypes.ConfigValue) error {
	logger := log.New()
	//err := r.publish(event, broker)
	//return err
	logger.Println("Riemann Publishing Started")
	var metrics []plugin.MetricType
	switch contentType {
	case plugin.SnapGOBContentType:
		dec := gob.NewDecoder(bytes.NewBuffer(content))
		if err := dec.Decode(&metrics); err != nil {
			logger.Printf("Error decoding: error=%v content=%v", err, content)
			return err
		}
	default:
		logger.Printf("Error unknown content type '%v'", contentType)
		return errors.New(fmt.Sprintf("Unknown content type '%s'", contentType))
	}
	logger.Printf("publishing %v to %v", metrics, config)
	for _, m := range metrics {
		e := createEvent(m, config)
		if err := r.publish(e, config["broker"].(ctypes.ConfigValueStr).Value); err != nil {
			logger.Println(err)
			return err
		}
	}
	return nil
}

// publish sends events to riemann
func (r *riemannPublisher) publish(event *raidman.Event, broker string) error {
	c, err := raidman.Dial("tcp", broker)
	if err != nil {
		return err
	}
	defer c.Close()
	return c.Send(event)
}

func createEvent(m plugin.MetricType, config map[string]ctypes.ConfigValue) *raidman.Event {
	return &raidman.Event{
		Host:    m.Tags()[core.STD_TAG_PLUGIN_RUNNING_ON],
		Service: m.Namespace().String(),
		Metric:  m.Data(),
	}
}

func handleErr(e error) {
	if e != nil {
		panic(e)
	}
}
<commit_msg>Fix send multiple events at once<commit_after>/*
http://www.apache.org/licenses/LICENSE-2.0.txt

Copyright 2015 Intel Corporation

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 riemann

import (
	"bytes"
	"encoding/gob"
	"errors"
	"fmt"

	log "github.com/Sirupsen/logrus"
	"github.com/amir/raidman"

	"github.com/intelsdi-x/snap/control/plugin"
	"github.com/intelsdi-x/snap/control/plugin/cpolicy"
	"github.com/intelsdi-x/snap/core"
	"github.com/intelsdi-x/snap/core/ctypes"
)

const (
	PluginName    = "riemann"
	PluginVersion = 9
	PluginType    = plugin.PublisherPluginType
)

// Meta returns the metadata details for the Riemann Publisher Plugin
func Meta() *plugin.PluginMeta {
	return plugin.NewPluginMeta(PluginName, PluginVersion, PluginType, []string{plugin.SnapGOBContentType}, []string{plugin.SnapGOBContentType})
}

type riemannPublisher struct{}

// NewRiemannPublisher does something cool
func NewRiemannPublisher() *riemannPublisher {
	return &riemannPublisher{}
}

// GetConfigPolicy returns the config policy for the Riemann Publisher Plugin
func (r *riemannPublisher) GetConfigPolicy() (*cpolicy.ConfigPolicy, error) {
	cp := cpolicy.New()
	config := cpolicy.NewPolicyNode()

	// Riemann server to publish event to
	r1, err := cpolicy.NewStringRule("broker", true)
	handleErr(err)
	r1.Description = "Broker in the format of broker-ip:port (ex: 192.168.1.1:5555)"

	config.Add(r1)
	cp.Add([]string{""}, config)
	return cp, nil
}

// Publish serializes the data and calls publish to send events to Riemann
func (r *riemannPublisher) Publish(contentType string, content []byte, config map[string]ctypes.ConfigValue) error {
	logger := log.New()
	//err := r.publish(event, broker)
	//return err
	logger.Println("Riemann Publishing Started")
	var metrics []plugin.MetricType
	switch contentType {
	case plugin.SnapGOBContentType:
		dec := gob.NewDecoder(bytes.NewBuffer(content))
		if err := dec.Decode(&metrics); err != nil {
			logger.Printf("Error decoding: error=%v content=%v", err, content)
			return err
		}
	default:
		logger.Printf("Error unknown content type '%v'", contentType)
		return errors.New(fmt.Sprintf("Unknown content type '%s'", contentType))
	}
	logger.Printf("publishing %v to %v", metrics, config)

	events := []*raidman.Event{}
	for _, m := range metrics {
		events = append(events, createEvent(m, config))
	}
	if err := r.publish(events, config["broker"].(ctypes.ConfigValueStr).Value); err != nil {
		logger.Println(err)
		return err
	}
	return nil
}

// publish sends events to riemann
func (r *riemannPublisher) publish(events []*raidman.Event, broker string) error {
	c, err := raidman.Dial("tcp", broker)
	if err != nil {
		return err
	}
	defer c.Close()
	return c.SendMulti(events)
}

func createEvent(m plugin.MetricType, config map[string]ctypes.ConfigValue) *raidman.Event {
	return &raidman.Event{
		Host:    m.Tags()[core.STD_TAG_PLUGIN_RUNNING_ON],
		Service: m.Namespace().String(),
		Metric:  m.Data(),
	}
}

func handleErr(e error) {
	if e != nil {
		panic(e)
	}
}
<|endoftext|>


<commit_before>package bot

import (
	"fmt"
	"log"
	"math/rand"
	"strconv"
	"strings"
	"time"

	"github.com/camd67/moebot/moebot_bot/bot/commands"
	"github.com/camd67/moebot/moebot_bot/util"
	"github.com/camd67/moebot/moebot_bot/util/db"

	"github.com/bwmarrin/discordgo"
)

const (
	version = "0.3"
)

var (
	// need to move this to the db
	allowedNonComServers = []string{"378336255030722570", "93799773856862208"}
	ComPrefix            string
	Config               = make(map[string]string)
	pinnedMessages       = make(map[string][]string)
	commandsMap          = make(map[string]commands.Command)
)

func SetupMoebot(session *discordgo.Session) {
	db.SetupDatabase(Config["dbPass"], Config["moeDataPass"])
	setupCommands()
	addHandlers(session)
}

func setupCommands() {
	commandsMap["POLL"] = commands.PollCommand{Checker: &commands.PermissionChecker{MasterId: Config["masterId"]}, PollsHandler: commands.NewPollsHandler()}
	commandsMap["PINMOVE"] = commands.PinMoveCommand{Checker: &commands.PermissionChecker{MasterId: Config["masterId"]}}
}

func addHandlers(discord *discordgo.Session) {
	discord.AddHandler(ready)
	discord.AddHandler(messageCreate)
	discord.AddHandler(guildMemberAdd)
	discord.AddHandler(messageReactionAdd)
	for _, com := range commandsMap {
		for _, h := range com.EventHandlers() {
			discord.AddHandler(h)
		}
	}
}

func guildMemberAdd(session *discordgo.Session, member *discordgo.GuildMemberAdd) {
	guild, err := session.Guild(member.GuildID)
	// temp ignore IHG + Salt
	if guild.ID == "84724034129907712" || guild.ID == "93799773856862208" {
		return
	}
	if err != nil {
		log.Println("Error when getting guild for user when joining guild", member)
		return
	}

	var starterRole *discordgo.Role
	for _, guildRole := range guild.Roles {
		if guildRole.Name == "Bell" {
			starterRole = guildRole
			break
		}
	}
	if starterRole == nil {
		log.Println("ERROR! Unable to find starter role for guild " + guild.Name)
		return
	}
	dmChannel, err := session.UserChannelCreate(member.User.ID)
	if err != nil {
		log.Println("ERROR! Unable to make DM channel with userID ", member.User.ID)
		return
	}
	session.ChannelMessageSend(dmChannel.ID, "Hello "+member.User.Mention()+" and welcome to "+guild.Name+"! Please read the #rules channel for more information")
	session.GuildMemberRoleAdd(member.GuildID, member.User.ID, starterRole.ID)
}

func messageCreate(session *discordgo.Session, message *discordgo.MessageCreate) {
	// bail out if we have any messages we want to ignore such as bot messages
	if message.Author.ID == session.State.User.ID || message.Author.Bot {
		return
	}

	channel, err := session.State.Channel(message.ChannelID)
	if err != nil {
		// missing channel
		log.Println("ERROR! Unable to get guild in messageCreate ", err, channel)
		return
	}

	guild, err := session.Guild(channel.GuildID)
	if err != nil {
		log.Println("ERROR! Unable to get guild in messageCreate ", err, guild)
		return
	}

	member, err := session.GuildMember(guild.ID, message.Author.ID)
	if err != nil {
		log.Println("ERROR! Unable to get member in messageCreate ", err, message)
		return
	}
	// temp ignore IHG
	if err != nil || guild.ID == "84724034129907712" {
		// missing guild
		return
	}
	// should change this to store the ID of the starting role
	var starterRole *discordgo.Role
	var oldStarterRole *discordgo.Role
	for _, guildRole := range guild.Roles {
		if strings.EqualFold(guildRole.Name, "Red Whistle") {
			starterRole = guildRole
		} else if strings.EqualFold(guildRole.Name, "Bell") {
			oldStarterRole = guildRole
		}
	}

	// ignore some common bot prefixes
	if !(strings.HasPrefix(message.Content, "->") || strings.HasPrefix(message.Content, "~") || strings.HasPrefix(message.Content, ComPrefix)) {
		changedUsers, err := handleVeteranMessage(member, guild.ID)
		if err != nil {
			session.ChannelMessageSend(Config["debugChannel"], fmt.Sprint("An error occurred when trying to update veteran users ", err))
		} else {
			for _, user := range changedUsers {
				session.ChannelMessageSend(user.SendTo, "Congrats "+util.UserIdToMention(user.UserUid)+" you can become a server veteran! Type `"+
					ComPrefix+" role veteran` In this channel.")
			}
		}
	}

	if strings.HasPrefix(message.Content, ComPrefix) {
		// should add a check here for command spam
		if oldStarterRole != nil && util.StrContains(member.Roles, oldStarterRole.ID, util.CaseSensitive) {
			// bail out to prevent any new users from using bot commands
			return
		}
		RunCommand(session, message.Message, guild, channel, member)
	} else if util.StrContains(allowedNonComServers, guild.ID, util.CaseSensitive) && oldStarterRole != nil {
		// message may have other bot related commands, but not with a prefix
		readRules := "I want to venture into the abyss"
		sanitizedMessage := util.MakeAlphaOnly(message.Content)
		if strings.HasPrefix(strings.ToUpper(sanitizedMessage), strings.ToUpper(readRules)) {
			if !util.StrContains(member.Roles, oldStarterRole.ID, util.CaseSensitive) {
				// bail out if the user doesn't have the first starter role. Preventing any duplicate role creations/DMs
				return
			}
			if starterRole == nil || oldStarterRole == nil {
				log.Println("ERROR! Unable to find roles for guild " + guild.Name)
				return
			}
			session.ChannelMessageSend(message.ChannelID, "Welcome "+message.Author.Mention()+"! We hope you enjoy your stay in our Discord server! Make sure to head over #announcements and #bot-stuff to see whats new and get your roles!")
			session.GuildMemberRoleAdd(guild.ID, member.User.ID, starterRole.ID)
			session.GuildMemberRoleRemove(guild.ID, member.User.ID, oldStarterRole.ID)
			log.Println("Updated user <" + member.User.Username + "> after reading the rules")
		}
	}
	// distribute tickets
	// temporarily disable ticket distribution
	//distributeTickets(guild, message, session, messageTime)
}

func messageReactionAdd(session *discordgo.Session, reactionAdd *discordgo.MessageReactionAdd) {
	// should make some local caches for channels and guilds...
	channel, err := session.Channel(reactionAdd.ChannelID)
	if err != nil {
		log.Println("Error trying to get channel", err)
		return
	}

	changedUsers, err := handleVeteranReaction(reactionAdd.UserID, channel.GuildID)
	if err != nil {
		session.ChannelMessageSend(Config["debugChannel"], fmt.Sprint("An error occurred when trying to update veteran users ", err))
	} else {
		for _, user := range changedUsers {
			session.ChannelMessageSend(user.SendTo, "Congrats "+util.UserIdToMention(user.UserUid)+" you can become a server veteran! Type `"+
				ComPrefix+" role veteran` In this channel.")
		}
	}
}

func distributeTickets(guild *discordgo.Guild, message *discordgo.MessageCreate, session *discordgo.Session, messageTime time.Time) {
	if false {
		const maxChance = 100
		const ticketChance = 5
		if rand.Int()%maxChance <= ticketChance {
			raffles, err := db.RaffleEntryQuery(message.Author.ID, guild.ID)
			if err != nil {
				session.ChannelMessageSend(Config["debugChannel"], "Error loading raffle information during ticket distribution"+fmt.Sprintf("%+v | %+v", guild, message))
				return
			}

			if len(raffles) != 1 {
				// if they're not in the raffle, bail out
				return
			}
			r := raffles[0]
			// check to see if their last ticket time is more than the cooldown
			if r.LastTicketUpdate+ticketCooldown > messageTime.UnixNano() {
				// they haven't waited enough, bail
				return
			}
			// they've won a ticket and passed the timestamp check, let them know and update db
			r.LastTicketUpdate = messageTime.UnixNano()
			db.RaffleEntryUpdate(r, 1)
			currTickets := r.TicketCount + 1
			session.ChannelMessageSend("378680855339728918", message.Author.Mention()+", congrats! You just earned another ticket! Your current tickets are: "+strconv.Itoa(currTickets))
		}
	}
}

func ready(session *discordgo.Session, event *discordgo.Ready) {
	status := ComPrefix + " help"
	err := session.UpdateStatus(0, status)
	if err != nil {
		log.Println("Error setting moebot status", err)
	}
	log.Println("Set moebot's status to", status)
}
<commit_msg>added setup handling<commit_after>package bot

import (
	"fmt"
	"log"
	"math/rand"
	"strconv"
	"strings"
	"time"

	"github.com/camd67/moebot/moebot_bot/bot/commands"
	"github.com/camd67/moebot/moebot_bot/util"
	"github.com/camd67/moebot/moebot_bot/util/db"

	"github.com/bwmarrin/discordgo"
)

const (
	version = "0.3"
)

var (
	// need to move this to the db
	allowedNonComServers = []string{"378336255030722570", "93799773856862208"}
	ComPrefix            string
	Config               = make(map[string]string)
	pinnedMessages       = make(map[string][]string)
	commandsMap          = make(map[string]commands.Command)
)

func SetupMoebot(session *discordgo.Session) {
	db.SetupDatabase(Config["dbPass"], Config["moeDataPass"])
	setupCommands(session)
	addHandlers(session)
}

func setupCommands(session *discordgo.Session) {
	commandsMap["POLL"] = commands.PollCommand{Checker: &commands.PermissionChecker{MasterId: Config["masterId"]}, PollsHandler: commands.NewPollsHandler()}
	commandsMap["PINMOVE"] = commands.PinMoveCommand{Checker: &commands.PermissionChecker{MasterId: Config["masterId"]}}

	for _, com := range commandsMap {
		com.Setup(session)
	}
}

func addHandlers(discord *discordgo.Session) {
	discord.AddHandler(ready)
	discord.AddHandler(messageCreate)
	discord.AddHandler(guildMemberAdd)
	discord.AddHandler(messageReactionAdd)
	for _, com := range commandsMap {
		for _, h := range com.EventHandlers() {
			discord.AddHandler(h)
		}
	}
}

func guildMemberAdd(session *discordgo.Session, member *discordgo.GuildMemberAdd) {
	guild, err := session.Guild(member.GuildID)
	// temp ignore IHG + Salt
	if guild.ID == "84724034129907712" || guild.ID == "93799773856862208" {
		return
	}
	if err != nil {
		log.Println("Error when getting guild for user when joining guild", member)
		return
	}

	var starterRole *discordgo.Role
	for _, guildRole := range guild.Roles {
		if guildRole.Name == "Bell" {
			starterRole = guildRole
			break
		}
	}
	if starterRole == nil {
		log.Println("ERROR! Unable to find starter role for guild " + guild.Name)
		return
	}
	dmChannel, err := session.UserChannelCreate(member.User.ID)
	if err != nil {
		log.Println("ERROR! Unable to make DM channel with userID ", member.User.ID)
		return
	}
	session.ChannelMessageSend(dmChannel.ID, "Hello "+member.User.Mention()+" and welcome to "+guild.Name+"! Please read the #rules channel for more information")
	session.GuildMemberRoleAdd(member.GuildID, member.User.ID, starterRole.ID)
}

func messageCreate(session *discordgo.Session, message *discordgo.MessageCreate) {
	// bail out if we have any messages we want to ignore such as bot messages
	if message.Author.ID == session.State.User.ID || message.Author.Bot {
		return
	}

	channel, err := session.State.Channel(message.ChannelID)
	if err != nil {
		// missing channel
		log.Println("ERROR! Unable to get guild in messageCreate ", err, channel)
		return
	}

	guild, err := session.Guild(channel.GuildID)
	if err != nil {
		log.Println("ERROR! Unable to get guild in messageCreate ", err, guild)
		return
	}

	member, err := session.GuildMember(guild.ID, message.Author.ID)
	if err != nil {
		log.Println("ERROR! Unable to get member in messageCreate ", err, message)
		return
	}
	// temp ignore IHG
	if err != nil || guild.ID == "84724034129907712" {
		// missing guild
		return
	}
	// should change this to store the ID of the starting role
	var starterRole *discordgo.Role
	var oldStarterRole *discordgo.Role
	for _, guildRole := range guild.Roles {
		if strings.EqualFold(guildRole.Name, "Red Whistle") {
			starterRole = guildRole
		} else if strings.EqualFold(guildRole.Name, "Bell") {
			oldStarterRole = guildRole
		}
	}

	// ignore some common bot prefixes
	if !(strings.HasPrefix(message.Content, "->") || strings.HasPrefix(message.Content, "~") || strings.HasPrefix(message.Content, ComPrefix)) {
		changedUsers, err := handleVeteranMessage(member, guild.ID)
		if err != nil {
			session.ChannelMessageSend(Config["debugChannel"], fmt.Sprint("An error occurred when trying to update veteran users ", err))
		} else {
			for _, user := range changedUsers {
				session.ChannelMessageSend(user.SendTo, "Congrats "+util.UserIdToMention(user.UserUid)+" you can become a server veteran! Type `"+
					ComPrefix+" role veteran` In this channel.")
			}
		}
	}

	if strings.HasPrefix(message.Content, ComPrefix) {
		// should add a check here for command spam
		if oldStarterRole != nil && util.StrContains(member.Roles, oldStarterRole.ID, util.CaseSensitive) {
			// bail out to prevent any new users from using bot commands
			return
		}
		RunCommand(session, message.Message, guild, channel, member)
	} else if util.StrContains(allowedNonComServers, guild.ID, util.CaseSensitive) && oldStarterRole != nil {
		// message may have other bot related commands, but not with a prefix
		readRules := "I want to venture into the abyss"
		sanitizedMessage := util.MakeAlphaOnly(message.Content)
		if strings.HasPrefix(strings.ToUpper(sanitizedMessage), strings.ToUpper(readRules)) {
			if !util.StrContains(member.Roles, oldStarterRole.ID, util.CaseSensitive) {
				// bail out if the user doesn't have the first starter role. Preventing any duplicate role creations/DMs
				return
			}
			if starterRole == nil || oldStarterRole == nil {
				log.Println("ERROR! Unable to find roles for guild " + guild.Name)
				return
			}
			session.ChannelMessageSend(message.ChannelID, "Welcome "+message.Author.Mention()+"! We hope you enjoy your stay in our Discord server! Make sure to head over #announcements and #bot-stuff to see whats new and get your roles!")
			session.GuildMemberRoleAdd(guild.ID, member.User.ID, starterRole.ID)
			session.GuildMemberRoleRemove(guild.ID, member.User.ID, oldStarterRole.ID)
			log.Println("Updated user <" + member.User.Username + "> after reading the rules")
		}
	}
	// distribute tickets
	// temporarily disable ticket distribution
	//distributeTickets(guild, message, session, messageTime)
}

func messageReactionAdd(session *discordgo.Session, reactionAdd *discordgo.MessageReactionAdd) {
	// should make some local caches for channels and guilds...
	channel, err := session.Channel(reactionAdd.ChannelID)
	if err != nil {
		log.Println("Error trying to get channel", err)
		return
	}

	changedUsers, err := handleVeteranReaction(reactionAdd.UserID, channel.GuildID)
	if err != nil {
		session.ChannelMessageSend(Config["debugChannel"], fmt.Sprint("An error occurred when trying to update veteran users ", err))
	} else {
		for _, user := range changedUsers {
			session.ChannelMessageSend(user.SendTo, "Congrats "+util.UserIdToMention(user.UserUid)+" you can become a server veteran! Type `"+
				ComPrefix+" role veteran` In this channel.")
		}
	}
}

func distributeTickets(guild *discordgo.Guild, message *discordgo.MessageCreate, session *discordgo.Session, messageTime time.Time) {
	if false {
		const maxChance = 100
		const ticketChance = 5
		if rand.Int()%maxChance <= ticketChance {
			raffles, err := db.RaffleEntryQuery(message.Author.ID, guild.ID)
			if err != nil {
				session.ChannelMessageSend(Config["debugChannel"], "Error loading raffle information during ticket distribution"+fmt.Sprintf("%+v | %+v", guild, message))
				return
			}

			if len(raffles) != 1 {
				// if they're not in the raffle, bail out
				return
			}
			r := raffles[0]
			// check to see if their last ticket time is more than the cooldown
			if r.LastTicketUpdate+ticketCooldown > messageTime.UnixNano() {
				// they haven't waited enough, bail
				return
			}
			// they've won a ticket and passed the timestamp check, let them know and update db
			r.LastTicketUpdate = messageTime.UnixNano()
			db.RaffleEntryUpdate(r, 1)
			currTickets := r.TicketCount + 1
			session.ChannelMessageSend("378680855339728918", message.Author.Mention()+", congrats! You just earned another ticket! Your current tickets are: "+strconv.Itoa(currTickets))
		}
	}
}

func ready(session *discordgo.Session, event *discordgo.Ready) {
	status := ComPrefix + " help"
	err := session.UpdateStatus(0, status)
	if err != nil {
		log.Println("Error setting moebot status", err)
	}
	log.Println("Set moebot's status to", status)
}
<|endoftext|>


<commit_before>// Copyright 2012 Dorival de Moraes Pedroso. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rnd

import "math"

// Stdphi implements φ(x), the standard probability density function
func Stdphi(x float64) float64 {
	return math.Exp(-x*x/2.0) / math.Sqrt2 / math.SqrtPi
}

// StdPhi implements Φ(x), the standard cumulative distribution function
func StdPhi(x float64) float64 {
	return (1.0 + math.Erf(x/math.Sqrt2)) / 2.0
}

// StdInvPhi implements Φ⁻¹(x), the inverse standard cumulative distribution function
func StdInvPhi(x float64) float64 {
	return ltqnorm(x)
}

// DistNormal implements the normal distribution
type DistNormal struct {

	// input
	Mu  float64 // μ: mean
	Sig float64 // σ: std deviation

	// auxiliary
	a float64 // 1 / (σ sqrt(2 π))
	b float64 // -1 / (2 σ²)
}

// set factory
func init() {
	distallocators[D_Nrm] = func() Distribution { return new(DistNormal) }
}

// CalcDerived compute derived/auxiliary quantities
func (o *DistNormal) CalcDerived() {
	o.a = 1.0 / (o.Sig * math.Sqrt2 * math.SqrtPi)
	o.b = -1.0 / (2.0 * o.Sig * o.Sig)
}

// Init initialises normal distribution
func (o *DistNormal) Init(p *VarData) error {
	o.Mu, o.Sig = p.M, p.S
	o.CalcDerived()
	return nil
}

// Pdf computes the probability density function @ x
func (o DistNormal) Pdf(x float64) float64 {
	return o.a * math.Exp(o.b*math.Pow(x-o.Mu, 2.0))
}

// Cdf computes the cumulative probability function @ x
func (o DistNormal) Cdf(x float64) float64 {
	return (1.0 + math.Erf((x-o.Mu)/(o.Sig*math.Sqrt2))) / 2.0
}
<commit_msg>rnd.Normal added<commit_after>// Copyright 2012 Dorival de Moraes Pedroso. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rnd

import (
	"math"
	"math/rand"
)

// Normal returns a random number belonging to a normal distribution
func Normal(μ, σ float64) float64 {
	return μ + σ*rand.NormFloat64()
}

// Stdphi implements φ(x), the standard probability density function
func Stdphi(x float64) float64 {
	return math.Exp(-x*x/2.0) / math.Sqrt2 / math.SqrtPi
}

// StdPhi implements Φ(x), the standard cumulative distribution function
func StdPhi(x float64) float64 {
	return (1.0 + math.Erf(x/math.Sqrt2)) / 2.0
}

// StdInvPhi implements Φ⁻¹(x), the inverse standard cumulative distribution function
func StdInvPhi(x float64) float64 {
	return ltqnorm(x)
}

// DistNormal implements the normal distribution
type DistNormal struct {

	// input
	Mu  float64 // μ: mean
	Sig float64 // σ: std deviation

	// auxiliary
	a float64 // 1 / (σ sqrt(2 π))
	b float64 // -1 / (2 σ²)
}

// set factory
func init() {
	distallocators[D_Nrm] = func() Distribution { return new(DistNormal) }
}

// CalcDerived compute derived/auxiliary quantities
func (o *DistNormal) CalcDerived() {
	o.a = 1.0 / (o.Sig * math.Sqrt2 * math.SqrtPi)
	o.b = -1.0 / (2.0 * o.Sig * o.Sig)
}

// Init initialises normal distribution
func (o *DistNormal) Init(p *VarData) error {
	o.Mu, o.Sig = p.M, p.S
	o.CalcDerived()
	return nil
}

// Pdf computes the probability density function @ x
func (o DistNormal) Pdf(x float64) float64 {
	return o.a * math.Exp(o.b*math.Pow(x-o.Mu, 2.0))
}

// Cdf computes the cumulative probability function @ x
func (o DistNormal) Cdf(x float64) float64 {
	return (1.0 + math.Erf((x-o.Mu)/(o.Sig*math.Sqrt2))) / 2.0
}
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc.
//
// 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 ytypes

import (
	"fmt"
	"reflect"
	"strings"

	log "github.com/golang/glog"
	"github.com/kylelemons/godebug/pretty"
	"github.com/openconfig/goyang/pkg/yang"
	"github.com/openconfig/ygot/util"
	"github.com/openconfig/ygot/ygot"
)

// Refer to: https://tools.ietf.org/html/rfc6020#section-7.5.

// validateContainer validates each of the values in the map, keyed by the list
// Key value, against the given list schema.
func validateContainer(schema *yang.Entry, value ygot.ValidatedGoStruct) util.Errors {
	var errors []error
	if util.IsValueNil(value) {
		return nil
	}
	// Check that the schema itself is valid.
	if err := validateContainerSchema(schema); err != nil {
		return util.NewErrs(err)
	}

	util.DbgPrint("validateContainer with value %v, type %T, schema name %s", util.ValueStrDebug(value), value, schema.Name)

	extraFields := make(map[string]interface{})

	switch reflect.TypeOf(value).Kind() {
	case reflect.Ptr:
		// Field exists in a struct but is unset.
		if reflect.ValueOf(value).IsNil() {
			return nil
		}
		structElems := reflect.ValueOf(value).Elem()
		structTypes := structElems.Type()

		for i := 0; i < structElems.NumField(); i++ {
			fieldType := structElems.Type().Field(i)
			fieldName := fieldType.Name
			fieldValue := structElems.Field(i).Interface()

			// Skip annotation fields when validating the schema.
			if util.IsYgotAnnotation(fieldType) {
				continue
			}

			cschema, err := util.ChildSchema(schema, structTypes.Field(i))
			switch {
			case err != nil:
				errors = util.AppendErr(errors, fmt.Errorf("%s: %v", fieldName, err))
				continue
			case cschema != nil:
				// Regular named child.
				if errs := Validate(cschema, fieldValue); errs != nil {
					errors = util.AppendErrs(errors, util.PrefixErrors(errs, cschema.Path()))
				}
			case !util.IsValueNilOrDefault(structElems.Field(i).Interface()):
				// Either an element in choice schema subtree, or bad field.
				// If the former, it will be found in the choice check below.
				extraFields[fieldName] = nil
			}
		}

		// Field names in the data tree belonging to Choice have the schema of
		// the elements of that choice. Hence, choice schemas must be checked
		// separately.
		for _, choiceSchema := range schema.Dir {
			if choiceSchema.IsChoice() {
				selected, errs := validateChoice(choiceSchema, value)
				for _, s := range selected {
					delete(extraFields, s)
				}
				if errs != nil {
					errors = util.AppendErrs(util.AppendErr(errors, fmt.Errorf("%s/", choiceSchema.Name)), errs)
				}
			}
		}

	default:
		errors = util.AppendErr(errors, fmt.Errorf("validateContainer expected struct type for %s (type %T), got %v", schema.Name, value, reflect.TypeOf(value).Kind()))
	}

	if len(extraFields) > 0 {
		errors = util.AppendErr(errors, fmt.Errorf("fields %v are not found in the container schema %s", stringMapSetToSlice(extraFields), schema.Name))
	}

	return util.UniqueErrors(errors)
}

// unmarshalContainer unmarshals a JSON tree into a struct.
//   schema is the schema of the schema node corresponding to the struct being
//     unmarshaled into.
//   parent is the parent struct, which must be a struct ptr.
//   jsonTree is a JSON data tree which must be a map[string]interface{}.
//   opts is the set of options that should be used when unmarshalling the JSON
//     into the supplied parent.
func unmarshalContainer(schema *yang.Entry, parent interface{}, jsonTree interface{}, enc Encoding, opts ...UnmarshalOpt) error {
	if util.IsValueNil(jsonTree) {
		return nil
	}

	// Check that the schema itself is valid.
	if err := validateContainerSchema(schema); err != nil {
		return err
	}

	util.DbgPrint("unmarshalContainer jsonTree %v, type %T, into parent type %T, schema name %s", util.ValueStrDebug(jsonTree), jsonTree, parent, schema.Name)

	// Since this is a container, the JSON data tree is a map.
	jt, ok := jsonTree.(map[string]interface{})
	if !ok {
		return fmt.Errorf("unmarshalContainer for schema %s: jsonTree %v: got type %T inside container, expect map[string]interface{}",
			schema.Name, util.ValueStr(jsonTree), jsonTree)
	}

	pvp := reflect.ValueOf(parent)
	if !util.IsValueStructPtr(pvp) {
		return fmt.Errorf("unmarshalContainer got parent type %T, expect struct ptr", parent)
	}

	return unmarshalStruct(schema, parent, jt, enc, opts...)
}

// unmarshalStruct unmarshals a JSON tree into a struct.
//   schema is the YANG schema of the node corresponding to the struct being
//     unmarshalled into.
//   parent is the parent struct, which must be a struct ptr.
//   jsonTree is a JSON data tree which must be a map[string]interface{}.
func unmarshalStruct(schema *yang.Entry, parent interface{}, jsonTree map[string]interface{}, enc Encoding, opts ...UnmarshalOpt) error {
	destv := reflect.ValueOf(parent).Elem()
	var allSchemaPaths [][]string

	// Range over the parent struct fields. For each field, check if the data
	// is present in the JSON tree and if so unmarshal it into the field.
	for i := 0; i < destv.NumField(); i++ {
		f := destv.Field(i)
		ft := destv.Type().Field(i)

		// Skip annotation fields since they do not have a schema.
		// TODO(robjs): Implement unmarshalling annotations.
		if util.IsYgotAnnotation(ft) {
			log.Infof("ignoring annotation field %s during unmarshalling, unsupported", ft.Name)
			// We need to find the paths that we should have unmarshalled here to avoid
			// throwing errors to users whilst there is a TODO above.
			paths, err := pathTagFromField(ft)
			if err != nil {
				return fmt.Errorf("cannot find JSON field names for annotation field %s, %v", ft.Name, err)
			}

			for _, s := range strings.Split(paths, "|") {
				pp := strings.Split(s, "/")
				allSchemaPaths = append(allSchemaPaths, []string{pp[len(pp)-1]})
			}
			continue
		}

		cschema, err := util.ChildSchema(schema, ft)
		if err != nil {
			return err
		}

		if cschema == nil {
			return fmt.Errorf("unmarshalContainer could not find schema for type %T, field name %s", parent, ft.Name)
		}

		// Store the data tree path of the current field. These will be used
		// at the end to ensure that there are no excess elements in the JSON
		// tree not covered by any data path.
		sp, err := dataTreePaths(schema, cschema, ft)
		if err != nil {
			return err
		}
		allSchemaPaths = append(allSchemaPaths, sp...)

		// If there are shadow schema paths, also add them to the allowlist
		// avoid an unmarshalling error.
		// NOTE: This is more permissive than ideal in that it doesn't
		// catch other types of non-compliance errors, i.e. if the JSON
		// shadow node is a container (should not occur under
		// OpenConfig YANG rules), or if the JSON cannot be
		// unmarshalled due to type mismatch.
		ssp, err := shadowDataTreePaths(schema, cschema, ft)
		if err != nil {
			return err
		}
		allSchemaPaths = append(allSchemaPaths, ssp...)

		jsonValue, err := getJSONTreeValForField(schema, cschema, ft, jsonTree)
		if err != nil {
			return err
		}

		if jsonValue == nil {
			util.DbgPrint("field %s paths %v not present in tree", ft.Name, sp)
			continue
		}

		util.DbgPrint("populating field %s type %s with paths %v.", ft.Name, ft.Type, sp)
		// Only create a new field if it is nil, otherwise update just the
		// fields that are in the data tree being passed to unmarshal, and
		// preserve all other existing values.
		if util.IsNilOrInvalidValue(f) {
			makeField(destv, ft)
		}

		p := parent
		switch {
		case util.IsUnkeyedList(cschema):
			// For unkeyed list, we must pass in the addr of the slice to be
			// able to append to it.
			p = f.Addr().Interface()
		case cschema.IsContainer() || cschema.IsList():
			// For list and container, the new parent is the field we just
			// created. For leaf and leaf-list, the parent is still the
			// current container.
			p = f.Interface()
		}
		if err := unmarshalGeneric(cschema, p, jsonValue, enc, opts...); err != nil {
			return err
		}
	}

	// Only check for missing fields if the IgnoreExtraFields option isn't specified.
	if !hasIgnoreExtraFields(opts) {
		// Go over all JSON fields to make sure that each one is covered
		// by a data path in the struct.
		if err := checkDataTreeAgainstPaths(jsonTree, allSchemaPaths); err != nil {
			return fmt.Errorf("parent container %s (type %T): %s", schema.Name, parent, err)
		}
	}

	util.DbgPrint("container after unmarshal:\n%s\n", pretty.Sprint(destv.Interface()))
	return nil
}

// validateContainerSchema validates the given container type schema. This is a
// quick check rather than a comprehensive validation against the RFC. It is
// assumed that such a validation is done when the schema is parsed from source
// YANG.
func validateContainerSchema(schema *yang.Entry) error {
	if schema == nil {
		return fmt.Errorf("container schema is nil")
	}
	if !schema.IsContainer() {
		return fmt.Errorf("container schema %s is not a container type", schema.Name)
	}

	return nil
}
<commit_msg>Remove log that is always generated for annotation fields. (#652)<commit_after>// Copyright 2017 Google Inc.
//
// 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 ytypes

import (
	"fmt"
	"reflect"
	"strings"

	"github.com/kylelemons/godebug/pretty"
	"github.com/openconfig/goyang/pkg/yang"
	"github.com/openconfig/ygot/util"
	"github.com/openconfig/ygot/ygot"
)

// Refer to: https://tools.ietf.org/html/rfc6020#section-7.5.

// validateContainer validates each of the values in the map, keyed by the list
// Key value, against the given list schema.
func validateContainer(schema *yang.Entry, value ygot.ValidatedGoStruct) util.Errors {
	var errors []error
	if util.IsValueNil(value) {
		return nil
	}
	// Check that the schema itself is valid.
	if err := validateContainerSchema(schema); err != nil {
		return util.NewErrs(err)
	}

	util.DbgPrint("validateContainer with value %v, type %T, schema name %s", util.ValueStrDebug(value), value, schema.Name)

	extraFields := make(map[string]interface{})

	switch reflect.TypeOf(value).Kind() {
	case reflect.Ptr:
		// Field exists in a struct but is unset.
		if reflect.ValueOf(value).IsNil() {
			return nil
		}
		structElems := reflect.ValueOf(value).Elem()
		structTypes := structElems.Type()

		for i := 0; i < structElems.NumField(); i++ {
			fieldType := structElems.Type().Field(i)
			fieldName := fieldType.Name
			fieldValue := structElems.Field(i).Interface()

			// Skip annotation fields when validating the schema.
			if util.IsYgotAnnotation(fieldType) {
				continue
			}

			cschema, err := util.ChildSchema(schema, structTypes.Field(i))
			switch {
			case err != nil:
				errors = util.AppendErr(errors, fmt.Errorf("%s: %v", fieldName, err))
				continue
			case cschema != nil:
				// Regular named child.
				if errs := Validate(cschema, fieldValue); errs != nil {
					errors = util.AppendErrs(errors, util.PrefixErrors(errs, cschema.Path()))
				}
			case !util.IsValueNilOrDefault(structElems.Field(i).Interface()):
				// Either an element in choice schema subtree, or bad field.
				// If the former, it will be found in the choice check below.
				extraFields[fieldName] = nil
			}
		}

		// Field names in the data tree belonging to Choice have the schema of
		// the elements of that choice. Hence, choice schemas must be checked
		// separately.
		for _, choiceSchema := range schema.Dir {
			if choiceSchema.IsChoice() {
				selected, errs := validateChoice(choiceSchema, value)
				for _, s := range selected {
					delete(extraFields, s)
				}
				if errs != nil {
					errors = util.AppendErrs(util.AppendErr(errors, fmt.Errorf("%s/", choiceSchema.Name)), errs)
				}
			}
		}

	default:
		errors = util.AppendErr(errors, fmt.Errorf("validateContainer expected struct type for %s (type %T), got %v", schema.Name, value, reflect.TypeOf(value).Kind()))
	}

	if len(extraFields) > 0 {
		errors = util.AppendErr(errors, fmt.Errorf("fields %v are not found in the container schema %s", stringMapSetToSlice(extraFields), schema.Name))
	}

	return util.UniqueErrors(errors)
}

// unmarshalContainer unmarshals a JSON tree into a struct.
//   schema is the schema of the schema node corresponding to the struct being
//     unmarshaled into.
//   parent is the parent struct, which must be a struct ptr.
//   jsonTree is a JSON data tree which must be a map[string]interface{}.
//   opts is the set of options that should be used when unmarshalling the JSON
//     into the supplied parent.
func unmarshalContainer(schema *yang.Entry, parent interface{}, jsonTree interface{}, enc Encoding, opts ...UnmarshalOpt) error {
	if util.IsValueNil(jsonTree) {
		return nil
	}

	// Check that the schema itself is valid.
	if err := validateContainerSchema(schema); err != nil {
		return err
	}

	util.DbgPrint("unmarshalContainer jsonTree %v, type %T, into parent type %T, schema name %s", util.ValueStrDebug(jsonTree), jsonTree, parent, schema.Name)

	// Since this is a container, the JSON data tree is a map.
	jt, ok := jsonTree.(map[string]interface{})
	if !ok {
		return fmt.Errorf("unmarshalContainer for schema %s: jsonTree %v: got type %T inside container, expect map[string]interface{}",
			schema.Name, util.ValueStr(jsonTree), jsonTree)
	}

	pvp := reflect.ValueOf(parent)
	if !util.IsValueStructPtr(pvp) {
		return fmt.Errorf("unmarshalContainer got parent type %T, expect struct ptr", parent)
	}

	return unmarshalStruct(schema, parent, jt, enc, opts...)
}

// unmarshalStruct unmarshals a JSON tree into a struct.
//   schema is the YANG schema of the node corresponding to the struct being
//     unmarshalled into.
//   parent is the parent struct, which must be a struct ptr.
//   jsonTree is a JSON data tree which must be a map[string]interface{}.
func unmarshalStruct(schema *yang.Entry, parent interface{}, jsonTree map[string]interface{}, enc Encoding, opts ...UnmarshalOpt) error {
	destv := reflect.ValueOf(parent).Elem()
	var allSchemaPaths [][]string

	// Range over the parent struct fields. For each field, check if the data
	// is present in the JSON tree and if so unmarshal it into the field.
	for i := 0; i < destv.NumField(); i++ {
		f := destv.Field(i)
		ft := destv.Type().Field(i)

		// Skip annotation fields since they do not have a schema.
		// TODO(robjs): Implement unmarshalling annotations.
		if util.IsYgotAnnotation(ft) {
			// We need to find the paths that we should have unmarshalled here to avoid
			// throwing errors to users whilst there is a TODO above.
			paths, err := pathTagFromField(ft)
			if err != nil {
				return fmt.Errorf("cannot find JSON field names for annotation field %s, %v", ft.Name, err)
			}

			for _, s := range strings.Split(paths, "|") {
				pp := strings.Split(s, "/")
				allSchemaPaths = append(allSchemaPaths, []string{pp[len(pp)-1]})
			}
			continue
		}

		cschema, err := util.ChildSchema(schema, ft)
		if err != nil {
			return err
		}

		if cschema == nil {
			return fmt.Errorf("unmarshalContainer could not find schema for type %T, field name %s", parent, ft.Name)
		}

		// Store the data tree path of the current field. These will be used
		// at the end to ensure that there are no excess elements in the JSON
		// tree not covered by any data path.
		sp, err := dataTreePaths(schema, cschema, ft)
		if err != nil {
			return err
		}
		allSchemaPaths = append(allSchemaPaths, sp...)

		// If there are shadow schema paths, also add them to the allowlist
		// avoid an unmarshalling error.
		// NOTE: This is more permissive than ideal in that it doesn't
		// catch other types of non-compliance errors, i.e. if the JSON
		// shadow node is a container (should not occur under
		// OpenConfig YANG rules), or if the JSON cannot be
		// unmarshalled due to type mismatch.
		ssp, err := shadowDataTreePaths(schema, cschema, ft)
		if err != nil {
			return err
		}
		allSchemaPaths = append(allSchemaPaths, ssp...)

		jsonValue, err := getJSONTreeValForField(schema, cschema, ft, jsonTree)
		if err != nil {
			return err
		}

		if jsonValue == nil {
			util.DbgPrint("field %s paths %v not present in tree", ft.Name, sp)
			continue
		}

		util.DbgPrint("populating field %s type %s with paths %v.", ft.Name, ft.Type, sp)
		// Only create a new field if it is nil, otherwise update just the
		// fields that are in the data tree being passed to unmarshal, and
		// preserve all other existing values.
		if util.IsNilOrInvalidValue(f) {
			makeField(destv, ft)
		}

		p := parent
		switch {
		case util.IsUnkeyedList(cschema):
			// For unkeyed list, we must pass in the addr of the slice to be
			// able to append to it.
			p = f.Addr().Interface()
		case cschema.IsContainer() || cschema.IsList():
			// For list and container, the new parent is the field we just
			// created. For leaf and leaf-list, the parent is still the
			// current container.
			p = f.Interface()
		}
		if err := unmarshalGeneric(cschema, p, jsonValue, enc, opts...); err != nil {
			return err
		}
	}

	// Only check for missing fields if the IgnoreExtraFields option isn't specified.
	if !hasIgnoreExtraFields(opts) {
		// Go over all JSON fields to make sure that each one is covered
		// by a data path in the struct.
		if err := checkDataTreeAgainstPaths(jsonTree, allSchemaPaths); err != nil {
			return fmt.Errorf("parent container %s (type %T): %s", schema.Name, parent, err)
		}
	}

	util.DbgPrint("container after unmarshal:\n%s\n", pretty.Sprint(destv.Interface()))
	return nil
}

// validateContainerSchema validates the given container type schema. This is a
// quick check rather than a comprehensive validation against the RFC. It is
// assumed that such a validation is done when the schema is parsed from source
// YANG.
func validateContainerSchema(schema *yang.Entry) error {
	if schema == nil {
		return fmt.Errorf("container schema is nil")
	}
	if !schema.IsContainer() {
		return fmt.Errorf("container schema %s is not a container type", schema.Name)
	}

	return nil
}
<|endoftext|>


<commit_before>package database

import (
	"database/sql"

	"github.com/nsec/askgod/api"
)

// GetScoreboard generates the current scoreboard
func (db *DB) GetScoreboard(team *api.AdminTeam) ([]api.ScoreboardEntry, error) {
	// Return a list of score entries
	resp := []api.ScoreboardEntry{}

	// Query all the scores from the database
	var rows *sql.Rows
	var err error

	if team == nil {
		rows, err = db.Query("SELECT team.id, team.country, team.name, team.website, COALESCE(SUM(score.value), 0) AS points, MAX(score.submit_time) FROM score RIGHT JOIN team ON team.id=score.teamid WHERE team.name != '' AND team.country != '' GROUP BY team.id ORDER BY points DESC;")
		if err != nil {
			return nil, err
		}
	} else {
		rows, err = db.Query("SELECT team.id, team.country, team.name, team.website, COALESCE(SUM(score.value), 0) AS points, MAX(score.submit_time) FROM score RIGHT JOIN team ON team.id=score.teamid WHERE team.id=$1 AND team.name != '' AND team.country != '' GROUP BY team.id ORDER BY points DESC;", team.ID)
		if err != nil {
			return nil, err
		}
	}
	defer rows.Close()

	// Iterate through the results
	for rows.Next() {
		row := api.ScoreboardEntry{}

		err := rows.Scan(&row.Team.ID, &row.Team.Country, &row.Team.Name, &row.Team.Website, &row.Value, &row.LastSubmitTime)
		if err != nil {
			return nil, err
		}

		resp = append(resp, row)
	}

	// Check for any error that might have happened
	err = rows.Err()
	if err != nil {
		return nil, err
	}

	return resp, nil
}
<commit_msg>askgod-server: Order scoreboard by team id if tied<commit_after>package database

import (
	"database/sql"

	"github.com/nsec/askgod/api"
)

// GetScoreboard generates the current scoreboard
func (db *DB) GetScoreboard(team *api.AdminTeam) ([]api.ScoreboardEntry, error) {
	// Return a list of score entries
	resp := []api.ScoreboardEntry{}

	// Query all the scores from the database
	var rows *sql.Rows
	var err error

	if team == nil {
		rows, err = db.Query("SELECT team.id, team.country, team.name, team.website, COALESCE(SUM(score.value), 0) AS points, MAX(score.submit_time) FROM score RIGHT JOIN team ON team.id=score.teamid WHERE team.name != '' AND team.country != '' GROUP BY team.id ORDER BY points DESC, team.id ASC;")
		if err != nil {
			return nil, err
		}
	} else {
		rows, err = db.Query("SELECT team.id, team.country, team.name, team.website, COALESCE(SUM(score.value), 0) AS points, MAX(score.submit_time) FROM score RIGHT JOIN team ON team.id=score.teamid WHERE team.id=$1 AND team.name != '' AND team.country != '' GROUP BY team.id ORDER BY points DESC, team.id ASC;", team.ID)
		if err != nil {
			return nil, err
		}
	}
	defer rows.Close()

	// Iterate through the results
	for rows.Next() {
		row := api.ScoreboardEntry{}

		err := rows.Scan(&row.Team.ID, &row.Team.Country, &row.Team.Name, &row.Team.Website, &row.Value, &row.LastSubmitTime)
		if err != nil {
			return nil, err
		}

		resp = append(resp, row)
	}

	// Check for any error that might have happened
	err = rows.Err()
	if err != nil {
		return nil, err
	}

	return resp, nil
}
<|endoftext|>