func (ns Slice) ToMap() map[string]struct{} {
	m := make(map[string]struct{})
	for _, nn := range ns {
		m[nn.Nodestr] = struct{}{}
	}
	return m
}
ToMap returns map[nodestr]struct{}{} for searching a node.
package node
import (
	"errors"
	"fmt"
	"log"
	"net"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"
	"github.com/shingetsu-gou/shingetsu-gou/cfg"
	"github.com/shingetsu-gou/shingetsu-gou/myself"
	"github.com/shingetsu-gou/shingetsu-gou/util"
)
type Node struct {
	Nodestr string
}
func MustNew(names []string) []*Node {
	ns := make([]*Node, len(names))
	for i, nn := range names {
		nn, err := New(nn)
		if err != nil {
			log.Fatal(err)
		}
		ns[i] = nn
	}
	return ns
}
func New(nodestr string) (*Node, error) {
	nodestr = strings.TrimSpace(nodestr)
	if nodestr == "" {
		err := errors.New("nodestr is empty")
		log.Println(err)
		return nil, err
	}
	if match, err := regexp.MatchString(`\d+/[^: ]+$`, nodestr); !match || err != nil {
		errr := errors.New(fmt.Sprintln("bad format", err, nodestr))
		return nil, errr
	}
	n := &Node{
		Nodestr: strings.Replace(nodestr, "+", "/", -1),
	}
	return n, nil
}
func (n *Node) urlopen(url string, timeout time.Duration, fn func(string) error) error {
	ua := "shinGETsuPlus/1.0alpha (Gou/" + cfg.Version + ")"
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		log.Println(err)
		return err
	}
	req.Header.Set("User-Agent", ua)
	transport := http.Transport{
		Dial: func(network, addr string) (net.Conn, error) {
			con, errr := net.DialTimeout(network, addr, timeout)
			if errr != nil {
				return nil, errr
			}
			errr = con.SetDeadline(time.Now().Add(time.Minute))
			return con, errr
		},
	}
	client := http.Client{
		Transport: &transport,
		Timeout:   timeout,
	}
	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
		return err
	}
	err = util.EachIOLine(resp.Body, func(line string, i int) error {
		return fn(line)
	})
	return err
}
func (n *Node) Equals(nn *Node) bool {
	if nn == nil {
		return false
	}
	return n.Nodestr == nn.Nodestr
}
func MakeNode(host, path string, port int) (*Node, error) {
	nodestr := net.JoinHostPort(host, strconv.Itoa(port)) + strings.Replace(path, "+", "/", -1)
	return New(nodestr)
}
func (n *Node) Toxstring() string {
	return strings.Replace(n.Nodestr, "/", "+", -1)
}
func (n *Node) Talk(message string, fn func(string) error) ([]string, error) {
	const defaultTimeout = 15 * time.Second  
	var res []string
	if fn == nil {
		fn = func(line string) error {
			res = append(res, line)
			return nil
		}
	}
	if !strings.HasPrefix(message, "/") {
		message = "/" + message
	}
	if n == nil {
		err := errors.New("n==nil")
		log.Println(err)
		return nil, err
	}
	msg := "http://" + n.Nodestr + message
	log.Println("Talk:", msg)
	err := n.urlopen(msg, defaultTimeout, fn)
	if err != nil {
		log.Println(msg, err)
	}
	return res, err
}
func (n *Node) Ping() (string, error) {
	res, err := n.Talk("/ping", nil)
	if err != nil {
		log.Println("/ping", n.Nodestr, err)
		return "", err
	}
	if len(res) == 2 && res[0] == "PONG" {
		log.Println("ponged,i am", res[1])
		myself.SetIP(res[1])
		return res[1], nil
	}
	log.Println("/ping", n.Nodestr, "error")
	return "", errors.New("connected,but not ponged")
}
func (n *Node) IsAllowed() bool {
	nodeAllow := util.NewRegexpList(cfg.NodeAllowFile)
	nodeDeny := util.NewRegexpList(cfg.NodeDenyFile)
	if !nodeAllow.Check(n.Nodestr) && nodeDeny.Check(n.Nodestr) {
		return false
	}
	return true
}
func (n *Node) Join() (*Node, error) {
	if !n.IsAllowed() {
		err := errors.New(fmt.Sprintln(n.Nodestr, "is not allowd"))
		return nil, err
	}
	res, err := n.Talk("/join/"+Me(true).Toxstring(), nil)
	if err != nil {
		return nil, err
	}
	log.Println(n.Nodestr, "response of Join:", res)
	switch len(res) {
	case 0:
		return nil, errors.New("illegal response")
	case 1:
		if res[0] != "WELCOME" {
			return nil, errors.New("not welcomed")
		}
		return nil, nil
	}
	nn, err := New(res[1])
	if err != nil {
		log.Println(err)
		return nil, err
	}
	if res[0] != "WELCOME" {
		err = errors.New("not welcomed")
	}
	return nn, err
}
func (n *Node) getNode() (*Node, error) {
	res, err := n.Talk("/node", nil)
	if err != nil {
		err := errors.New(fmt.Sprintln("/node", n.Nodestr, "error"))
		return nil, err
	}
	if len(res) == 0 {
		return nil, errors.New("no response")
	}
	return New(res[0])
}
func (n *Node) Bye() bool {
	res, err := n.Talk("/bye/"+Me(true).Toxstring(), nil)
	if err != nil {
		log.Println("/bye", n.Nodestr, "error")
		return false
	}
	return len(res) > 0 && (res[0] == "BYEBYE")
}
func (n *Node) GetherNodes() []*Node {
	ns := map[string]*Node{
		n.Nodestr: n,
	}
	for i := 0; i < 10; i++ {
		var mutex sync.Mutex
		var wg sync.WaitGroup
		for _, nn := range ns {
			wg.Add(1)
			go func(nn *Node) {
				defer wg.Done()
				NewN, err := nn.getNode()
				if err != nil {
					log.Println(err)
					return
				}
				mutex.Lock()
				ns[NewN.Nodestr] = NewN
				mutex.Unlock()
			}(nn)
		}
		wg.Wait()
		log.Println("iteration", i, ",# of nodes:", len(ns))
	}
	nss := make([]*Node, len(ns))
	var i int
	for _, nn := range ns {
		nss[i] = nn
		i++
	}
	return nss
}
type Slice []*Node
func NewSlice(names []string) Slice {
	var ns Slice
	for _, nn := range names {
		nn, err := New(nn)
		if err != nil {
			log.Println(err)
			continue
		}
		ns = append(ns, nn)
	}
	return ns
}
func (ns Slice) Len() int {
	return len(ns)
}
func (ns Slice) Has(n *Node) bool {
	return util.HasString(ns.GetNodestrSlice(), n.Nodestr)
}
func (ns Slice) GetNodestrSlice() []string {
	s := make([]string, ns.Len())
	for i, v := range ns {
		s[i] = v.Nodestr
	}
	return s
}