<commit_before>package tak

import "errors"

type MoveType byte

const (
	PlaceFlat MoveType = 1 + iota
	PlaceStanding
	PlaceCapstone
	SlideLeft
	SlideRight
	SlideUp
	SlideDown
)

const TypeMask MoveType = 0xf

type Move struct {
	X, Y   int
	Type   MoveType
	Slides []byte
}

func (m *Move) Equal(rhs *Move) bool {
	if m.X != rhs.X || m.Y != rhs.Y {
		return false
	}
	if m.Type != rhs.Type {
		return false
	}
	if m.Type < SlideLeft {
		return true
	}
	if len(m.Slides) != len(rhs.Slides) {
		return false
	}
	for i, s := range m.Slides {
		if rhs.Slides[i] != s {
			return false
		}
	}
	return true
}

var (
	ErrOccupied       = errors.New("position is occupied")
	ErrIllegalSlide   = errors.New("illegal slide")
	ErrNoCapstone     = errors.New("capstone has already been played")
	ErrIllegalOpening = errors.New("illegal opening move")
)

func (p *Position) Move(m *Move) (*Position, error) {
	return p.MovePreallocated(m, nil)
}

func (p *Position) MovePreallocated(m *Move, next *Position) (*Position, error) {
	if next == nil {
		next = alloc(p)
	} else {
		copyPosition(p, next)
	}
	next.move++
	var place Piece
	dx, dy := 0, 0
	switch m.Type {
	case PlaceFlat:
		place = MakePiece(p.ToMove(), Flat)
	case PlaceStanding:
		place = MakePiece(p.ToMove(), Standing)
	case PlaceCapstone:
		place = MakePiece(p.ToMove(), Capstone)
	case SlideLeft:
		dx = -1
	case SlideRight:
		dx = 1
	case SlideUp:
		dy = 1
	case SlideDown:
		dy = -1
	}
	if p.move < 2 {
		if place.Kind() != Flat {
			return nil, ErrIllegalOpening
		}
		place = MakePiece(place.Color().Flip(), place.Kind())
	}
	i := uint(m.X + m.Y*p.Size())
	if place != 0 {
		if (p.White|p.Black)&(1<<i) != 0 {
			return nil, ErrOccupied
		}

		var stones *byte
		switch place.Kind() {
		case Capstone:
			if p.ToMove() == Black {
				stones = &next.blackCaps
			} else {
				stones = &next.whiteCaps
			}
			next.Caps |= (1 << i)
		case Standing:
			next.Standing |= (1 << i)
			fallthrough
		case Flat:
			if place.Color() == Black {
				stones = &next.blackStones
			} else {
				stones = &next.whiteStones
			}
		}
		if *stones <= 0 {
			return nil, ErrNoCapstone
		}
		*stones--
		if place.Color() == White {
			next.White |= (1 << i)
		} else {
			next.Black |= (1 << i)
		}
		next.Height[i]++
		next.analyze()
		return next, nil
	}

	ct := uint(0)
	for _, c := range m.Slides {
		ct += uint(c)
	}
	if ct > uint(p.cfg.Size) || ct < 1 || ct > uint(p.Height[i]) {
		return nil, ErrIllegalSlide
	}
	if p.ToMove() == White && p.White&(1<<i) == 0 {
		return nil, ErrIllegalSlide
	}
	if p.ToMove() == Black && p.Black&(1<<i) == 0 {
		return nil, ErrIllegalSlide
	}

	top := p.Top(m.X, m.Y)
	stack := p.Stacks[i] << 1
	if top.Color() == Black {
		stack |= 1
	}

	next.Caps &= ^(1 << i)
	next.Standing &= ^(1 << i)
	if uint(next.Height[i]) == ct {
		next.White &= ^(1 << i)
		next.Black &= ^(1 << i)
	} else {
		if stack&(1<<ct) == 0 {
			next.White |= (1 << i)
			next.Black &= ^(1 << i)
		} else {
			next.Black |= (1 << i)
			next.White &= ^(1 << i)
		}
	}
	next.hash ^= next.hashAt(i)
	next.Stacks[i] >>= ct
	next.Height[i] -= uint8(ct)
	next.hash ^= next.hashAt(i)

	x, y := m.X, m.Y
	for _, c := range m.Slides {
		x += dx
		y += dy
		if x < 0 || x >= next.cfg.Size ||
			y < 0 || y >= next.cfg.Size {
			return nil, ErrIllegalSlide
		}
		if int(c) < 1 || uint(c) > ct {
			return nil, ErrIllegalSlide
		}
		i = uint(x + y*p.Size())
		switch {
		case next.Caps&(1<<i) != 0:
			return nil, ErrIllegalSlide
		case next.Standing&(1<<i) != 0:
			if ct != 1 || top.Kind() != Capstone {
				return nil, ErrIllegalSlide
			}
			next.Standing &= ^(1 << i)
		}
		next.hash ^= next.hashAt(i)
		if next.White&(1<<i) != 0 {
			next.Stacks[i] <<= 1
		} else if next.Black&(1<<i) != 0 {
			next.Stacks[i] <<= 1
			next.Stacks[i] |= 1
		}
		drop := (stack >> (ct - uint(c-1))) & ((1 << (c - 1)) - 1)
		next.Stacks[i] = next.Stacks[i]<<(c-1) | drop
		next.Height[i] += c
		next.hash ^= next.hashAt(i)
		if stack&(1<<(ct-uint(c))) != 0 {
			next.Black |= (1 << i)
			next.White &= ^(1 << i)
		} else {
			next.Black &= ^(1 << i)
			next.White |= (1 << i)
		}
		ct -= uint(c)
		if ct == 0 {
			switch top.Kind() {
			case Capstone:
				next.Caps |= (1 << i)
			case Standing:
				next.Standing |= (1 << i)
			}
		}
	}

	next.analyze()
	return next, nil
}

var slides [][][]byte

func init() {
	slides = make([][][]byte, 10)
	for s := 1; s <= 8; s++ {
		slides[s] = calculateSlides(s)
	}
}

func calculateSlides(stack int) [][]byte {
	var out [][]byte
	for i := byte(1); i <= byte(stack); i++ {
		out = append(out, []byte{i})
		for _, sub := range slides[stack-int(i)] {
			t := make([]byte, len(sub)+1)
			t[0] = i
			copy(t[1:], sub)
			out = append(out, t)
		}
	}
	return out
}

func (p *Position) AllMoves(moves []Move) []Move {
	next := p.ToMove()
	cap := false
	if next == White {
		cap = p.whiteCaps > 0
	} else {
		cap = p.blackCaps > 0
	}
	for x := 0; x < p.cfg.Size; x++ {
		for y := 0; y < p.cfg.Size; y++ {
			i := uint(y*p.cfg.Size + x)
			if p.Height[i] == 0 {
				moves = append(moves, Move{x, y, PlaceFlat, nil})
				if p.move >= 2 {
					moves = append(moves, Move{x, y, PlaceStanding, nil})
					if cap {
						moves = append(moves, Move{x, y, PlaceCapstone, nil})
					}
				}
				continue
			}
			if p.move < 2 {
				continue
			}
			if next == White && p.White&(1<<i) == 0 {
				continue
			} else if next == Black && p.Black&(1<<i) == 0 {
				continue
			}

			type dircnt struct {
				d MoveType
				c int
			}
			dirs := [4]dircnt{
				{SlideLeft, x},
				{SlideRight, p.cfg.Size - x - 1},
				{SlideDown, y},
				{SlideUp, p.cfg.Size - y - 1},
			}
			for _, d := range dirs {
				h := p.Height[i]
				if h > uint8(p.cfg.Size) {
					h = uint8(p.cfg.Size)
				}
				for _, s := range slides[h] {
					if len(s) <= d.c {
						moves = append(moves, Move{x, y, d.d, s})
					}
				}
			}
		}
	}

	return moves
}
<commit_msg>add a helper to Move<commit_after>package tak

import "errors"

type MoveType byte

const (
	PlaceFlat MoveType = 1 + iota
	PlaceStanding
	PlaceCapstone
	SlideLeft
	SlideRight
	SlideUp
	SlideDown
)

const TypeMask MoveType = 0xf

type Move struct {
	X, Y   int
	Type   MoveType
	Slides []byte
}

func (m *Move) Equal(rhs *Move) bool {
	if m.X != rhs.X || m.Y != rhs.Y {
		return false
	}
	if m.Type != rhs.Type {
		return false
	}
	if m.Type < SlideLeft {
		return true
	}
	if len(m.Slides) != len(rhs.Slides) {
		return false
	}
	for i, s := range m.Slides {
		if rhs.Slides[i] != s {
			return false
		}
	}
	return true
}

func (m *Move) Dest() (int, int) {
	switch m.Type {
	case PlaceFlat, PlaceStanding, PlaceCapstone:
		return m.X, m.Y
	case SlideLeft:
		return m.X - len(m.Slides), m.Y
	case SlideRight:
		return m.X + len(m.Slides), m.Y
	case SlideUp:
		return m.X, m.Y + len(m.Slides)
	case SlideDown:
		return m.X, m.Y - len(m.Slides)
	}
	panic("bad type")
}

var (
	ErrOccupied       = errors.New("position is occupied")
	ErrIllegalSlide   = errors.New("illegal slide")
	ErrNoCapstone     = errors.New("capstone has already been played")
	ErrIllegalOpening = errors.New("illegal opening move")
)

func (p *Position) Move(m *Move) (*Position, error) {
	return p.MovePreallocated(m, nil)
}

func (p *Position) MovePreallocated(m *Move, next *Position) (*Position, error) {
	if next == nil {
		next = alloc(p)
	} else {
		copyPosition(p, next)
	}
	next.move++
	var place Piece
	dx, dy := 0, 0
	switch m.Type {
	case PlaceFlat:
		place = MakePiece(p.ToMove(), Flat)
	case PlaceStanding:
		place = MakePiece(p.ToMove(), Standing)
	case PlaceCapstone:
		place = MakePiece(p.ToMove(), Capstone)
	case SlideLeft:
		dx = -1
	case SlideRight:
		dx = 1
	case SlideUp:
		dy = 1
	case SlideDown:
		dy = -1
	}
	if p.move < 2 {
		if place.Kind() != Flat {
			return nil, ErrIllegalOpening
		}
		place = MakePiece(place.Color().Flip(), place.Kind())
	}
	i := uint(m.X + m.Y*p.Size())
	if place != 0 {
		if (p.White|p.Black)&(1<<i) != 0 {
			return nil, ErrOccupied
		}

		var stones *byte
		switch place.Kind() {
		case Capstone:
			if p.ToMove() == Black {
				stones = &next.blackCaps
			} else {
				stones = &next.whiteCaps
			}
			next.Caps |= (1 << i)
		case Standing:
			next.Standing |= (1 << i)
			fallthrough
		case Flat:
			if place.Color() == Black {
				stones = &next.blackStones
			} else {
				stones = &next.whiteStones
			}
		}
		if *stones <= 0 {
			return nil, ErrNoCapstone
		}
		*stones--
		if place.Color() == White {
			next.White |= (1 << i)
		} else {
			next.Black |= (1 << i)
		}
		next.Height[i]++
		next.analyze()
		return next, nil
	}

	ct := uint(0)
	for _, c := range m.Slides {
		ct += uint(c)
	}
	if ct > uint(p.cfg.Size) || ct < 1 || ct > uint(p.Height[i]) {
		return nil, ErrIllegalSlide
	}
	if p.ToMove() == White && p.White&(1<<i) == 0 {
		return nil, ErrIllegalSlide
	}
	if p.ToMove() == Black && p.Black&(1<<i) == 0 {
		return nil, ErrIllegalSlide
	}

	top := p.Top(m.X, m.Y)
	stack := p.Stacks[i] << 1
	if top.Color() == Black {
		stack |= 1
	}

	next.Caps &= ^(1 << i)
	next.Standing &= ^(1 << i)
	if uint(next.Height[i]) == ct {
		next.White &= ^(1 << i)
		next.Black &= ^(1 << i)
	} else {
		if stack&(1<<ct) == 0 {
			next.White |= (1 << i)
			next.Black &= ^(1 << i)
		} else {
			next.Black |= (1 << i)
			next.White &= ^(1 << i)
		}
	}
	next.hash ^= next.hashAt(i)
	next.Stacks[i] >>= ct
	next.Height[i] -= uint8(ct)
	next.hash ^= next.hashAt(i)

	x, y := m.X, m.Y
	for _, c := range m.Slides {
		x += dx
		y += dy
		if x < 0 || x >= next.cfg.Size ||
			y < 0 || y >= next.cfg.Size {
			return nil, ErrIllegalSlide
		}
		if int(c) < 1 || uint(c) > ct {
			return nil, ErrIllegalSlide
		}
		i = uint(x + y*p.Size())
		switch {
		case next.Caps&(1<<i) != 0:
			return nil, ErrIllegalSlide
		case next.Standing&(1<<i) != 0:
			if ct != 1 || top.Kind() != Capstone {
				return nil, ErrIllegalSlide
			}
			next.Standing &= ^(1 << i)
		}
		next.hash ^= next.hashAt(i)
		if next.White&(1<<i) != 0 {
			next.Stacks[i] <<= 1
		} else if next.Black&(1<<i) != 0 {
			next.Stacks[i] <<= 1
			next.Stacks[i] |= 1
		}
		drop := (stack >> (ct - uint(c-1))) & ((1 << (c - 1)) - 1)
		next.Stacks[i] = next.Stacks[i]<<(c-1) | drop
		next.Height[i] += c
		next.hash ^= next.hashAt(i)
		if stack&(1<<(ct-uint(c))) != 0 {
			next.Black |= (1 << i)
			next.White &= ^(1 << i)
		} else {
			next.Black &= ^(1 << i)
			next.White |= (1 << i)
		}
		ct -= uint(c)
		if ct == 0 {
			switch top.Kind() {
			case Capstone:
				next.Caps |= (1 << i)
			case Standing:
				next.Standing |= (1 << i)
			}
		}
	}

	next.analyze()
	return next, nil
}

var slides [][][]byte

func init() {
	slides = make([][][]byte, 10)
	for s := 1; s <= 8; s++ {
		slides[s] = calculateSlides(s)
	}
}

func calculateSlides(stack int) [][]byte {
	var out [][]byte
	for i := byte(1); i <= byte(stack); i++ {
		out = append(out, []byte{i})
		for _, sub := range slides[stack-int(i)] {
			t := make([]byte, len(sub)+1)
			t[0] = i
			copy(t[1:], sub)
			out = append(out, t)
		}
	}
	return out
}

func (p *Position) AllMoves(moves []Move) []Move {
	next := p.ToMove()
	cap := false
	if next == White {
		cap = p.whiteCaps > 0
	} else {
		cap = p.blackCaps > 0
	}
	for x := 0; x < p.cfg.Size; x++ {
		for y := 0; y < p.cfg.Size; y++ {
			i := uint(y*p.cfg.Size + x)
			if p.Height[i] == 0 {
				moves = append(moves, Move{x, y, PlaceFlat, nil})
				if p.move >= 2 {
					moves = append(moves, Move{x, y, PlaceStanding, nil})
					if cap {
						moves = append(moves, Move{x, y, PlaceCapstone, nil})
					}
				}
				continue
			}
			if p.move < 2 {
				continue
			}
			if next == White && p.White&(1<<i) == 0 {
				continue
			} else if next == Black && p.Black&(1<<i) == 0 {
				continue
			}

			type dircnt struct {
				d MoveType
				c int
			}
			dirs := [4]dircnt{
				{SlideLeft, x},
				{SlideRight, p.cfg.Size - x - 1},
				{SlideDown, y},
				{SlideUp, p.cfg.Size - y - 1},
			}
			for _, d := range dirs {
				h := p.Height[i]
				if h > uint8(p.cfg.Size) {
					h = uint8(p.cfg.Size)
				}
				for _, s := range slides[h] {
					if len(s) <= d.c {
						moves = append(moves, Move{x, y, d.d, s})
					}
				}
			}
		}
	}

	return moves
}
<|endoftext|>


<commit_before>package dummy

import (
	"github.com/intelsdilabs/pulse/control/plugin"
)

const (
	Name    = "dummy"
	Version = 1
)

// Dummy collector implementation used for testing
type Dummy struct {
}

func (f *Dummy) Collect(args plugin.CollectorArgs, reply *plugin.CollectorReply) error {
	return nil
}

func Meta() *plugin.PluginMeta {
	m := new(plugin.PluginMeta)
	m.Name = Name
	m.Version = Version
	return m
}

func ConfigPolicy() *plugin.ConfigPolicy {
	c := new(plugin.ConfigPolicy)
	return c
}
<commit_msg>Added<commit_after>package dummy

import (
	"github.com/intelsdilabs/pulse/control/plugin"
)

const (
	Name    = "dummy"
	Version = 1
	Type    = "collector"
)

// Dummy collector implementation used for testing
type Dummy struct {
}

func (f *Dummy) Collect(args plugin.CollectorArgs, reply *plugin.CollectorReply) error {
	return nil
}

func Meta() *plugin.PluginMeta {
	m := new(plugin.PluginMeta)
	m.Name = Name
	m.Version = Version
	return m
}

func ConfigPolicy() *plugin.ConfigPolicy {
	c := new(plugin.ConfigPolicy)
	return c
}
<|endoftext|>


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

package fun

import (
	"math"

	"github.com/cpmech/gosl/chk"
)

// InterpCubic computes a cubic polynomial to perform interpolation either using 4 points
// or 3 points and a known derivative
type InterpCubic struct {
	A, B, C, D float64 // coefficients of polynomial
	TolDen     float64 // tolerance to avoid zero denominator
}

// NewInterpCubic returns a new object
func NewInterpCubic() (o *InterpCubic) {
	o = new(InterpCubic)
	o.TolDen = 1e-15
	return
}

// F computes y = f(x) curve
func (o *InterpCubic) F(x float64) float64 {
	return o.A*x*x*x + o.B*x*x + o.C*x + o.D
}

// G computes y' = df/x|(x) curve
func (o *InterpCubic) G(x float64) float64 {
	return 3.0*o.A*x*x + 2.0*o.B*x + o.C
}

// Critical returns the critical points
//   xmin -- x @ min and y(xmin)
//   xmax -- x @ max and y(xmax)
//   xifl -- x @ inflection point and y(ifl)
//   hasMin, hasMax, hasIfl -- flags telling what is available
func (o *InterpCubic) Critical() (xmin, xmax, xifl float64, hasMin, hasMax, hasIfl bool) {
	delBy4 := o.B*o.B - 3.0*o.A*o.C
	if delBy4 < 0 {
		return // cubic function is strictly monotonic
	}
	den := 3.0 * o.A
	xifl = -o.B / den
	hasIfl = true
	if delBy4 != 0.0 {
		xmin = (-o.B + math.Sqrt(delBy4)) / den
		xmax = (-o.B - math.Sqrt(delBy4)) / den
		if o.F(xmin) > o.F(xmax) {
			xmin, xmax = xmax, xmin
		}
		hasMin = true
		hasMax = true
	}
	return
}

// Fit4points fits polynomial to 3 points
//   (x0, y0) -- first point
//   (x1, y1) -- second point
//   (x2, y2) -- third point
//   (x3, y3) -- fourth point
func (o *InterpCubic) Fit4points(x0, y0, x1, y1, x2, y2, x3, y3 float64) (err error) {
	z0, z1, z2, z3 := x0*x0, x1*x1, x2*x2, x3*x3
	w0, w1, w2, w3 := z0*x0, z1*x1, z2*x2, z3*x3
	den := w0*((x2-x3)*z1+x3*z2-x2*z3+x1*(z3-z2)) + w1*(x2*z3-x3*z2) + x0*((w3-w2)*z1-w3*z2+w1*(z2-z3)+w2*z3) + x1*(w3*z2-w2*z3) + (w2*x3-w3*x2)*z1 + ((w2-w3)*x1+w3*x2-w2*x3+w1*(x3-x2))*z0
	if math.Abs(den) < o.TolDen {
		return chk.Err("Cannot fit 4 points because denominator=%g is near zero.\n\t(x0,y0)=(%g,%g)\t(x1,y1)=(%g,%g)\t(x2,y2)=(%g,%g)\t(x3,y3)=(%g,%g)\n", x0, y0, x1, y1, x2, y2, x3, y3)
	}
	o.A = -((x1*(y3-y2)-x2*y3+x3*y2+(x2-x3)*y1)*z0 + (x2*y3-x3*y2)*z1 + y1*(x3*z2-x2*z3) + y0*(x2*z3+x1*(z2-z3)-x3*z2+(x3-x2)*z1) + x1*(y2*z3-y3*z2) + x0*(y1*(z3-z2)-y2*z3+y3*z2+(y2-y3)*z1)) / den
	o.B = ((w1*(x3-x2)-w2*x3+w3*x2+(w2-w3)*x1)*y0 + (w2*x3-w3*x2)*y1 + x1*(w3*y2-w2*y3) + x0*(w2*y3+w1*(y2-y3)-w3*y2+(w3-w2)*y1) + w1*(x2*y3-x3*y2) + w0*(x1*(y3-y2)-x2*y3+x3*y2+(x2-x3)*y1)) / den
	o.C = ((w1*(y3-y2)-w2*y3+w3*y2+(w2-w3)*y1)*z0 + (w2*y3-w3*y2)*z1 + y1*(w3*z2-w2*z3) + y0*(w2*z3+w1*(z2-z3)-w3*z2+(w3-w2)*z1) + w1*(y2*z3-y3*z2) + w0*(y1*(z3-z2)-y2*z3+y3*z2+(y2-y3)*z1)) / den
	o.D = ((w1*(x3*y2-x2*y3)+x1*(w2*y3-w3*y2)+(w3*x2-w2*x3)*y1)*z0 + y0*(w1*(x2*z3-x3*z2)+x1*(w3*z2-w2*z3)+(w2*x3-w3*x2)*z1) + x0*(w1*(y3*z2-y2*z3)+y1*(w2*z3-w3*z2)+(w3*y2-w2*y3)*z1) + w0*(x1*(y2*z3-y3*z2)+y1*(x3*z2-x2*z3)+(x2*y3-x3*y2)*z1)) / den
	return
}

// Fit3pointsD fits polynomial to 3 points and known derivative
//   (x0, y0) -- first point
//   (x1, y1) -- second point
//   (x2, y2) -- third point
//   (x3, d3) -- derivative @ x3
func (o *InterpCubic) Fit3pointsD(x0, y0, x1, y1, x2, y2, x3, d3 float64) (err error) {
	z0, z1, z2, z3 := x0*x0, x1*x1, x2*x2, x3*x3
	w0, w1, w2 := z0*x0, z1*x1, z2*x2
	den := x0*(2*w1*x3-2*w2*x3-3*z1*z3+3*z2*z3) + x1*(2*w2*x3-3*z2*z3) + z1*(3*x2*z3-w2) + z0*(-w1+w2+3*x1*z3-3*x2*z3) + w1*(z2-2*x2*x3) + w0*(-2*x1*x3+2*x2*x3+z1-z2)
	if math.Abs(den) < o.TolDen {
		return chk.Err("Cannot fit 3 points and known derivative because denominator=%g is near zero.\n\t(x0,y0)=(%g,%g)\t(x1,y1)=(%g,%g)\t(x2,y2)=(%g,%g)\t(x3,d3)=(%g,%g)\n", x0, y0, x1, y1, x2, y2, x3, d3)
	}
	o.A = -(-2*x1*x3*y2 + x0*(2*x3*y2-2*x3*y1) + (y1-y2)*z0 + y2*z1 + y1*(2*x2*x3-z2) + y0*(z2-z1-2*x2*x3+2*x1*x3)) / den
	o.B = (w0*(y1-y2) + w1*y2 - 3*x1*y2*z3 + y0*(-3*x2*z3+3*x1*z3+w2-w1) + y1*(3*x2*z3-w2) + x0*(3*y2*z3-3*y1*z3)) / den
	o.C = (-2*w1*x3*y2 + w0*(2*x3*y2-2*x3*y1) + 3*y2*z1*z3 + z0*(3*y1*z3-3*y2*z3) + y1*(2*w2*x3-3*z2*z3) + y0*(3*z2*z3-3*z1*z3-2*w2*x3+2*w1*x3)) / den
	o.D = -(w0*(y1*(z2-2*x2*x3)-y2*z1+2*x1*x3*y2) + z0*(y1*(3*x2*z3-w2)-3*x1*y2*z3+w1*y2) + x0*(y1*(2*w2*x3-3*z2*z3)+3*y2*z1*z3-2*w1*x3*y2) + y0*(x1*(3*z2*z3-2*w2*x3)+z1*(w2-3*x2*z3)+w1*(2*x2*x3-z2))) / den
	return
}
<commit_msg>Fix error message<commit_after>// Copyright 2016 The Gosl Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package fun

import (
	"math"

	"github.com/cpmech/gosl/chk"
)

// InterpCubic computes a cubic polynomial to perform interpolation either using 4 points
// or 3 points and a known derivative
type InterpCubic struct {
	A, B, C, D float64 // coefficients of polynomial
	TolDen     float64 // tolerance to avoid zero denominator
}

// NewInterpCubic returns a new object
func NewInterpCubic() (o *InterpCubic) {
	o = new(InterpCubic)
	o.TolDen = 1e-15
	return
}

// F computes y = f(x) curve
func (o *InterpCubic) F(x float64) float64 {
	return o.A*x*x*x + o.B*x*x + o.C*x + o.D
}

// G computes y' = df/x|(x) curve
func (o *InterpCubic) G(x float64) float64 {
	return 3.0*o.A*x*x + 2.0*o.B*x + o.C
}

// Critical returns the critical points
//   xmin -- x @ min and y(xmin)
//   xmax -- x @ max and y(xmax)
//   xifl -- x @ inflection point and y(ifl)
//   hasMin, hasMax, hasIfl -- flags telling what is available
func (o *InterpCubic) Critical() (xmin, xmax, xifl float64, hasMin, hasMax, hasIfl bool) {
	delBy4 := o.B*o.B - 3.0*o.A*o.C
	if delBy4 < 0 {
		return // cubic function is strictly monotonic
	}
	den := 3.0 * o.A
	xifl = -o.B / den
	hasIfl = true
	if delBy4 != 0.0 {
		xmin = (-o.B + math.Sqrt(delBy4)) / den
		xmax = (-o.B - math.Sqrt(delBy4)) / den
		if o.F(xmin) > o.F(xmax) {
			xmin, xmax = xmax, xmin
		}
		hasMin = true
		hasMax = true
	}
	return
}

// Fit4points fits polynomial to 3 points
//   (x0, y0) -- first point
//   (x1, y1) -- second point
//   (x2, y2) -- third point
//   (x3, y3) -- fourth point
func (o *InterpCubic) Fit4points(x0, y0, x1, y1, x2, y2, x3, y3 float64) (err error) {
	z0, z1, z2, z3 := x0*x0, x1*x1, x2*x2, x3*x3
	w0, w1, w2, w3 := z0*x0, z1*x1, z2*x2, z3*x3
	den := w0*((x2-x3)*z1+x3*z2-x2*z3+x1*(z3-z2)) + w1*(x2*z3-x3*z2) + x0*((w3-w2)*z1-w3*z2+w1*(z2-z3)+w2*z3) + x1*(w3*z2-w2*z3) + (w2*x3-w3*x2)*z1 + ((w2-w3)*x1+w3*x2-w2*x3+w1*(x3-x2))*z0
	if math.Abs(den) < o.TolDen {
		return chk.Err("Cannot fit 4 points because denominator=%g is near zero.\n\t(x0,y0)=(%g,%g)\n\t(x1,y1)=(%g,%g)\n\t(x2,y2)=(%g,%g)\n\t(x3,y3)=(%g,%g)\n", den, x0, y0, x1, y1, x2, y2, x3, y3)
	}
	o.A = -((x1*(y3-y2)-x2*y3+x3*y2+(x2-x3)*y1)*z0 + (x2*y3-x3*y2)*z1 + y1*(x3*z2-x2*z3) + y0*(x2*z3+x1*(z2-z3)-x3*z2+(x3-x2)*z1) + x1*(y2*z3-y3*z2) + x0*(y1*(z3-z2)-y2*z3+y3*z2+(y2-y3)*z1)) / den
	o.B = ((w1*(x3-x2)-w2*x3+w3*x2+(w2-w3)*x1)*y0 + (w2*x3-w3*x2)*y1 + x1*(w3*y2-w2*y3) + x0*(w2*y3+w1*(y2-y3)-w3*y2+(w3-w2)*y1) + w1*(x2*y3-x3*y2) + w0*(x1*(y3-y2)-x2*y3+x3*y2+(x2-x3)*y1)) / den
	o.C = ((w1*(y3-y2)-w2*y3+w3*y2+(w2-w3)*y1)*z0 + (w2*y3-w3*y2)*z1 + y1*(w3*z2-w2*z3) + y0*(w2*z3+w1*(z2-z3)-w3*z2+(w3-w2)*z1) + w1*(y2*z3-y3*z2) + w0*(y1*(z3-z2)-y2*z3+y3*z2+(y2-y3)*z1)) / den
	o.D = ((w1*(x3*y2-x2*y3)+x1*(w2*y3-w3*y2)+(w3*x2-w2*x3)*y1)*z0 + y0*(w1*(x2*z3-x3*z2)+x1*(w3*z2-w2*z3)+(w2*x3-w3*x2)*z1) + x0*(w1*(y3*z2-y2*z3)+y1*(w2*z3-w3*z2)+(w3*y2-w2*y3)*z1) + w0*(x1*(y2*z3-y3*z2)+y1*(x3*z2-x2*z3)+(x2*y3-x3*y2)*z1)) / den
	return
}

// Fit3pointsD fits polynomial to 3 points and known derivative
//   (x0, y0) -- first point
//   (x1, y1) -- second point
//   (x2, y2) -- third point
//   (x3, d3) -- derivative @ x3
func (o *InterpCubic) Fit3pointsD(x0, y0, x1, y1, x2, y2, x3, d3 float64) (err error) {
	z0, z1, z2, z3 := x0*x0, x1*x1, x2*x2, x3*x3
	w0, w1, w2 := z0*x0, z1*x1, z2*x2
	den := x0*(2*w1*x3-2*w2*x3-3*z1*z3+3*z2*z3) + x1*(2*w2*x3-3*z2*z3) + z1*(3*x2*z3-w2) + z0*(-w1+w2+3*x1*z3-3*x2*z3) + w1*(z2-2*x2*x3) + w0*(-2*x1*x3+2*x2*x3+z1-z2)
	if math.Abs(den) < o.TolDen {
		return chk.Err("Cannot fit 3 points and known derivative because denominator=%g is near zero.\n\t(x0,y0)=(%g,%g)\n\t(x1,y1)=(%g,%g)\n\t(x2,y2)=(%g,%g)\n\t(x3,d3)=(%g,%g)\n", den, x0, y0, x1, y1, x2, y2, x3, d3)
	}
	o.A = -(-2*x1*x3*y2 + x0*(2*x3*y2-2*x3*y1) + (y1-y2)*z0 + y2*z1 + y1*(2*x2*x3-z2) + y0*(z2-z1-2*x2*x3+2*x1*x3)) / den
	o.B = (w0*(y1-y2) + w1*y2 - 3*x1*y2*z3 + y0*(-3*x2*z3+3*x1*z3+w2-w1) + y1*(3*x2*z3-w2) + x0*(3*y2*z3-3*y1*z3)) / den
	o.C = (-2*w1*x3*y2 + w0*(2*x3*y2-2*x3*y1) + 3*y2*z1*z3 + z0*(3*y1*z3-3*y2*z3) + y1*(2*w2*x3-3*z2*z3) + y0*(3*z2*z3-3*z1*z3-2*w2*x3+2*w1*x3)) / den
	o.D = -(w0*(y1*(z2-2*x2*x3)-y2*z1+2*x1*x3*y2) + z0*(y1*(3*x2*z3-w2)-3*x1*y2*z3+w1*y2) + x0*(y1*(2*w2*x3-3*z2*z3)+3*y2*z1*z3-2*w1*x3*y2) + y0*(x1*(3*z2*z3-2*w2*x3)+z1*(w2-3*x2*z3)+w1*(2*x2*x3-z2))) / den
	return
}
<|endoftext|>


<commit_before>package ganglia

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"os"
	"regexp"
	"runtime"
	"strconv"
	"sync"
	"syscall"
	"time"

	"github.com/fastly/go-utils/debug"
	"github.com/fastly/go-utils/stopper"
	"github.com/fastly/go-utils/vlog"
	"github.com/jbuchbinder/go-gmetric/gmetric"
)

const (
	String = gmetric.VALUE_STRING
	Ushort = gmetric.VALUE_UNSIGNED_SHORT
	Short  = gmetric.VALUE_SHORT
	Uint   = gmetric.VALUE_UNSIGNED_INT
	Int    = gmetric.VALUE_INT
	Float  = gmetric.VALUE_FLOAT
	Double = gmetric.VALUE_DOUBLE
)

var (
	GmondConfig string
	Interval    time.Duration

	gmondChannelRe  = regexp.MustCompile("udp_send_channel\\s*{([^}]+)}")
	gmondHostPortRe = regexp.MustCompile("(host|port)\\s*=\\s*(\\S+)")

	globalReporter struct {
		sync.Once
		*Reporter
	}
)

func init() {
	flag.StringVar(&GmondConfig, "gmond-config", "/etc/ganglia/gmond.conf", "location of gmond.conf")
	flag.DurationVar(&Interval, "ganglia-interval", 9*time.Second, "time between gmetric updates")
}

type gmetricSample struct {
	value interface{}
	when  time.Time
}
type Reporter struct {
	*stopper.ChanStopper
	prefix    string
	callbacks []ReporterCallback
	previous  map[string]gmetricSample
	groupName string
}

// MetricSender takes the following parameters:
//   name: an arbitrary metric name
//   value: the metric's current value
//   metricType: one of GmetricString, GmetricUshort, GmetricShort, GmetricUint, GmetricInt, GmetricFloat, or GmetricDouble
//   units: a label to include on the metric's Y axis
//   rate: if true, send the rate relative to the last sample instead of an absolute value
type MetricSender func(name string, value string, metricType uint32, units string, rate bool)

type ReporterCallback func(MetricSender)

// Gmetric returns a global Reporter that clients may hook into by
// calling AddCallback.
func Gmetric() *Reporter {
	globalReporter.Do(func() {
		globalReporter.Reporter = NewGangliaReporter(Interval)
		globalReporter.AddCallback(CommonGmetrics)
	})
	return globalReporter.Reporter
}

// Convenience wrapper for Gmetric().AddCallback():
//
//   AddGmetrics(func(gmetric MetricSender) {
// 	   gmetric("profit", "1000000.00", GmetricFloat, "dollars", true)
//   })
func AddGmetrics(callback ReporterCallback) {
	Gmetric().AddCallback(callback)
}

func NewGmetric() (*gmetric.Gmetric, error) {
	b, err := ioutil.ReadFile(GmondConfig)
	if err != nil {
		return nil, err
	}
	stanzas := gmondChannelRe.FindAllStringSubmatch(string(b), -1)
	if len(stanzas) == 0 {
		return nil, fmt.Errorf("No udp_send_channel stanzas found in %s", GmondConfig)
	}

	servers := make([]gmetric.GmetricServer, 0)
	for _, stanza := range stanzas {
		var host, port string
		for _, match := range gmondHostPortRe.FindAllStringSubmatch(stanza[1], 2) {
			if match[1] == "host" {
				host = match[2]
			} else if match[1] == "port" {
				port = match[2]
			}
		}
		if host == "" || port == "" {
			return nil, fmt.Errorf("Missing host or port from %s stanza %q", GmondConfig, stanza[0])
		}
		portNum, err := strconv.Atoi(port)
		if err != nil {
			return nil, err
		}
		ips, err := net.LookupIP(host)
		if err != nil {
			return nil, err
		}
		for _, ip := range ips {
			vlog.VLogf("Reporting to Ganglia server at %s:%d", ip, portNum)
			servers = append(servers, gmetric.GmetricServer{ip, portNum})
		}
	}

	// see http://sourceforge.net/apps/trac/ganglia/wiki/gmetric_spoofing
	hostname, _ := os.Hostname()
	spoofName := fmt.Sprintf("%s:%s", hostname, hostname)

	gm := gmetric.Gmetric{Spoof: spoofName}
	for _, server := range servers {
		gm.AddServer(server)
	}
	return &gm, nil
}

// NewGangliaReporter returns a Reporter object which calls callback every
// interval with the given group name. callback is passed a Gmetric whose
// servers are initialized from the hosts gmond.conf. Calling Stop on the
// Reporter will cease its operation.
func NewGangliaReporter(interval time.Duration) *Reporter {
	return NewGangliaReporterWithOptions(interval, "", false)
}

// NewGangliaReporterWithOptions is NewGangliaReporter with the groupName
// and verbose parameters explicit.
func NewGangliaReporterWithOptions(interval time.Duration, groupName string, verbose bool) *Reporter {
	// set before the call to NewGmetric so VLogf in NewGmetric works properly
	vlog.Verbose = verbose
	gm, err := NewGmetric()
	if err != nil {
		vlog.VLogfQuiet("ganglia", "Couldn't start Ganglia reporter: %s", err)
		return nil
	} else if gm == nil {
		return nil
	}
	stopper := stopper.NewChanStopper()
	gr := &Reporter{stopper, "", make([]ReporterCallback, 0), make(map[string]gmetricSample), groupName}
	go func() {
		defer stopper.Done()
		for {
			select {
			case <-stopper.Chan:
				return
			case <-time.After(interval):
				go func() {
					// SendMetric "opens" and "closes" UDP connections each
					// time, but since we expect the callback to send several
					// metrics at once, avoid that here.
					conns := gm.OpenConnections()
					n := 0
					sender := func(name string, value string, metricType uint32, units string, rate bool) {
						v := value
						if rate {
							prev, exists := gr.previous[name]
							units += "/sec"

							now := time.Now()

							switch metricType {
							case Ushort, Short, Uint, Int:
								i, err := strconv.Atoi(value)
								if err != nil {
									vlog.VLogfQuiet(name, "Value %q doesn't look like an int: %s", value, err)
									return
								}
								gr.previous[name] = gmetricSample{i, now}
								if !exists {
									return
								}
								delta := i - prev.value.(int)
								elapsed := time.Now().Sub(prev.when).Seconds()
								v = fmt.Sprint(float64(delta) / elapsed)
								// upgrade to a float to avoid loss of precision
								metricType = Float

							case Float, Double:
								f, err := strconv.ParseFloat(value, 64)
								if err != nil {
									vlog.VLogfQuiet(name, "Value %q doesn't look like a float: %s", value, err)
									return
								}
								gr.previous[name] = gmetricSample{f, now}
								if !exists {
									return
								}
								delta := f - prev.value.(float64)
								elapsed := time.Now().Sub(prev.when).Seconds()
								v = fmt.Sprint(delta / elapsed)

							case String:
								vlog.VLogfQuiet(name, "Can't compute deltas for string metric %q", value)
								return
							}
						}

						n++
						gm.SendMetricPackets(
							gr.prefix+name, v, metricType, units,
							gmetric.SLOPE_BOTH,
							uint32(interval.Seconds()), // tmax is the expected reporting interval
							0, // dmax is the time to keep values in tsdb; 0 means forever
							groupName,
							gmetric.PACKET_BOTH, conns,
						)
						if debug.On() {
							if rate {
								log.Printf("gmetric: name=%q, rate=%q, value=%q, type=%d, units=%q, slope=%d, tmax=%d, dmax=%v, group=%q, packet=%d",
									gr.prefix+name, v, value, metricType, units, gmetric.SLOPE_BOTH,
									uint32(interval.Seconds()), 0, groupName, gmetric.PACKET_BOTH,
								)
							} else {
								log.Printf("gmetric: name=%q, value=%q, type=%d, units=%q, slope=%d, tmax=%d, dmax=%v, group=%q, packet=%d",
									gr.prefix+name, v, metricType, units, gmetric.SLOPE_BOTH,
									uint32(interval.Seconds()), 0, groupName, gmetric.PACKET_BOTH,
								)
							}
						}
					}
					defer gm.CloseConnections(conns)
					for _, callback := range gr.callbacks {
						callback(sender)
					}
					if debug.On() {
						log.Printf("Published %d metrics to Ganglia", n)
					}
				}()
			}
		}
	}()
	return gr
}

func (gr *Reporter) AddCallback(callback ReporterCallback) {
	if gr == nil {
		return
	}
	gr.callbacks = append(gr.callbacks, callback)
}

func (gr *Reporter) SetPrefix(prefix string) {
	if gr == nil {
		return
	}
	gr.prefix = prefix
}

func (g *Reporter) Stop() {
	if g == nil {
		return
	}
	g.Stop()
}

func CommonGmetrics(gmetric MetricSender) {
	gmetric("goroutines", fmt.Sprintf("%d", runtime.NumGoroutine()), Uint, "num", false)

	var mem runtime.MemStats
	runtime.ReadMemStats(&mem)
	gmetric("mem_alloc", fmt.Sprintf("%d", mem.Alloc), Uint, "bytes", false)
	gmetric("mem_sys", fmt.Sprintf("%d", mem.Sys), Uint, "bytes", false)
	gmetric("mem_gc_pause_last", fmt.Sprintf("%.6f", float64(mem.PauseNs[(mem.NumGC+255)%256])/1e6), Float, "ms", false)
	var gcPauseMax uint64
	for _, v := range mem.PauseNs {
		if v > gcPauseMax {
			gcPauseMax = v
		}
	}
	gmetric("mem_gc_pause_max", fmt.Sprintf("%.6f", float64(gcPauseMax)/1e6), Float, "ms", false)
	gmetric("mem_gc_pause_total", fmt.Sprintf("%.6f", float64(mem.PauseTotalNs)/1e6), Float, "ms", true)
	since := time.Now().Sub(time.Unix(0, int64(mem.LastGC))).Seconds()
	gmetric("mem_gc_pause_since", fmt.Sprintf("%.6f", since), Float, "sec", false)

	var r syscall.Rusage
	if syscall.Getrusage(syscall.RUSAGE_SELF, &r) == nil {
		gmetric("rusage_utime", fmt.Sprintf("%.6f", float64(r.Utime.Nano())/1e9), Float, "cpusecs", true)
		gmetric("rusage_stime", fmt.Sprintf("%.6f", float64(r.Stime.Nano())/1e9), Float, "cpusecs", true)
	}
}
<commit_msg>add configure for reporter<commit_after>package ganglia

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"os"
	"regexp"
	"runtime"
	"strconv"
	"sync"
	"syscall"
	"time"

	"github.com/fastly/go-utils/debug"
	"github.com/fastly/go-utils/stopper"
	"github.com/fastly/go-utils/vlog"
	"github.com/jbuchbinder/go-gmetric/gmetric"
)

const (
	String = gmetric.VALUE_STRING
	Ushort = gmetric.VALUE_UNSIGNED_SHORT
	Short  = gmetric.VALUE_SHORT
	Uint   = gmetric.VALUE_UNSIGNED_INT
	Int    = gmetric.VALUE_INT
	Float  = gmetric.VALUE_FLOAT
	Double = gmetric.VALUE_DOUBLE
)

var (
	GmondConfig string
	Interval    time.Duration

	gmondChannelRe  = regexp.MustCompile("udp_send_channel\\s*{([^}]+)}")
	gmondHostPortRe = regexp.MustCompile("(host|port)\\s*=\\s*(\\S+)")

	globalReporter struct {
		sync.Once
		*Reporter
	}
)

func init() {
	flag.StringVar(&GmondConfig, "gmond-config", "/etc/ganglia/gmond.conf", "location of gmond.conf")
	flag.DurationVar(&Interval, "ganglia-interval", 9*time.Second, "time between gmetric updates")
}

type gmetricSample struct {
	value interface{}
	when  time.Time
}
type Reporter struct {
	*stopper.ChanStopper
	prefix    string
	callbacks []ReporterCallback
	previous  map[string]gmetricSample
	groupName string
}

// MetricSender takes the following parameters:
//   name: an arbitrary metric name
//   value: the metric's current value
//   metricType: one of GmetricString, GmetricUshort, GmetricShort, GmetricUint, GmetricInt, GmetricFloat, or GmetricDouble
//   units: a label to include on the metric's Y axis
//   rate: if true, send the rate relative to the last sample instead of an absolute value
type MetricSender func(name string, value string, metricType uint32, units string, rate bool)

type ReporterCallback func(MetricSender)

// Gmetric returns a global Reporter that clients may hook into by
// calling AddCallback.
func Gmetric() *Reporter {
	globalReporter.Do(func() {
		globalReporter.Reporter = NewGangliaReporter(Interval)
		globalReporter.AddCallback(CommonGmetrics)
	})
	return globalReporter.Reporter
}

// Configure sets group name of a reporter and the verbose logging flag.
// It returns the reporter itself.
func (r *Reporter) Configure(groupName string, verbose bool) *Reporter {
	vlog.Verbose = verbose
	r.groupName = groupName
	return r
}

// Convenience wrapper for Gmetric().AddCallback():
//
//   AddGmetrics(func(gmetric MetricSender) {
// 	   gmetric("profit", "1000000.00", GmetricFloat, "dollars", true)
//   })
func AddGmetrics(callback ReporterCallback) {
	Gmetric().AddCallback(callback)
}

func NewGmetric() (*gmetric.Gmetric, error) {
	b, err := ioutil.ReadFile(GmondConfig)
	if err != nil {
		return nil, err
	}
	stanzas := gmondChannelRe.FindAllStringSubmatch(string(b), -1)
	if len(stanzas) == 0 {
		return nil, fmt.Errorf("No udp_send_channel stanzas found in %s", GmondConfig)
	}

	servers := make([]gmetric.GmetricServer, 0)
	for _, stanza := range stanzas {
		var host, port string
		for _, match := range gmondHostPortRe.FindAllStringSubmatch(stanza[1], 2) {
			if match[1] == "host" {
				host = match[2]
			} else if match[1] == "port" {
				port = match[2]
			}
		}
		if host == "" || port == "" {
			return nil, fmt.Errorf("Missing host or port from %s stanza %q", GmondConfig, stanza[0])
		}
		portNum, err := strconv.Atoi(port)
		if err != nil {
			return nil, err
		}
		ips, err := net.LookupIP(host)
		if err != nil {
			return nil, err
		}
		for _, ip := range ips {
			vlog.VLogf("Reporting to Ganglia server at %s:%d", ip, portNum)
			servers = append(servers, gmetric.GmetricServer{ip, portNum})
		}
	}

	// see http://sourceforge.net/apps/trac/ganglia/wiki/gmetric_spoofing
	hostname, _ := os.Hostname()
	spoofName := fmt.Sprintf("%s:%s", hostname, hostname)

	gm := gmetric.Gmetric{Spoof: spoofName}
	for _, server := range servers {
		gm.AddServer(server)
	}
	return &gm, nil
}

// NewGangliaReporter returns a Reporter object which calls callback every
// interval with the given group name. callback is passed a Gmetric whose
// servers are initialized from the hosts gmond.conf. Calling Stop on the
// Reporter will cease its operation.
func NewGangliaReporter(interval time.Duration) *Reporter {
	return NewGangliaReporterWithOptions(interval, "", false)
}

// NewGangliaReporterWithOptions is NewGangliaReporter with the groupName
// and verbose parameters explicit.
func NewGangliaReporterWithOptions(interval time.Duration, groupName string, verbose bool) *Reporter {
	// set before the call to NewGmetric so VLogf in NewGmetric works properly
	vlog.Verbose = verbose
	gm, err := NewGmetric()
	if err != nil {
		vlog.VLogfQuiet("ganglia", "Couldn't start Ganglia reporter: %s", err)
		return nil
	} else if gm == nil {
		return nil
	}
	stopper := stopper.NewChanStopper()
	gr := &Reporter{stopper, "", make([]ReporterCallback, 0), make(map[string]gmetricSample), groupName}
	go func() {
		defer stopper.Done()
		for {
			select {
			case <-stopper.Chan:
				return
			case <-time.After(interval):
				go func() {
					// SendMetric "opens" and "closes" UDP connections each
					// time, but since we expect the callback to send several
					// metrics at once, avoid that here.
					conns := gm.OpenConnections()
					n := 0
					sender := func(name string, value string, metricType uint32, units string, rate bool) {
						v := value
						if rate {
							prev, exists := gr.previous[name]
							units += "/sec"

							now := time.Now()

							switch metricType {
							case Ushort, Short, Uint, Int:
								i, err := strconv.Atoi(value)
								if err != nil {
									vlog.VLogfQuiet(name, "Value %q doesn't look like an int: %s", value, err)
									return
								}
								gr.previous[name] = gmetricSample{i, now}
								if !exists {
									return
								}
								delta := i - prev.value.(int)
								elapsed := time.Now().Sub(prev.when).Seconds()
								v = fmt.Sprint(float64(delta) / elapsed)
								// upgrade to a float to avoid loss of precision
								metricType = Float

							case Float, Double:
								f, err := strconv.ParseFloat(value, 64)
								if err != nil {
									vlog.VLogfQuiet(name, "Value %q doesn't look like a float: %s", value, err)
									return
								}
								gr.previous[name] = gmetricSample{f, now}
								if !exists {
									return
								}
								delta := f - prev.value.(float64)
								elapsed := time.Now().Sub(prev.when).Seconds()
								v = fmt.Sprint(delta / elapsed)

							case String:
								vlog.VLogfQuiet(name, "Can't compute deltas for string metric %q", value)
								return
							}
						}

						n++
						gm.SendMetricPackets(
							gr.prefix+name, v, metricType, units,
							gmetric.SLOPE_BOTH,
							uint32(interval.Seconds()), // tmax is the expected reporting interval
							0, // dmax is the time to keep values in tsdb; 0 means forever
							groupName,
							gmetric.PACKET_BOTH, conns,
						)
						if debug.On() {
							if rate {
								log.Printf("gmetric: name=%q, rate=%q, value=%q, type=%d, units=%q, slope=%d, tmax=%d, dmax=%v, group=%q, packet=%d",
									gr.prefix+name, v, value, metricType, units, gmetric.SLOPE_BOTH,
									uint32(interval.Seconds()), 0, groupName, gmetric.PACKET_BOTH,
								)
							} else {
								log.Printf("gmetric: name=%q, value=%q, type=%d, units=%q, slope=%d, tmax=%d, dmax=%v, group=%q, packet=%d",
									gr.prefix+name, v, metricType, units, gmetric.SLOPE_BOTH,
									uint32(interval.Seconds()), 0, groupName, gmetric.PACKET_BOTH,
								)
							}
						}
					}
					defer gm.CloseConnections(conns)
					for _, callback := range gr.callbacks {
						callback(sender)
					}
					if debug.On() {
						log.Printf("Published %d metrics to Ganglia", n)
					}
				}()
			}
		}
	}()
	return gr
}

func (gr *Reporter) AddCallback(callback ReporterCallback) {
	if gr == nil {
		return
	}
	gr.callbacks = append(gr.callbacks, callback)
}

func (gr *Reporter) SetPrefix(prefix string) {
	if gr == nil {
		return
	}
	gr.prefix = prefix
}

func (g *Reporter) Stop() {
	if g == nil {
		return
	}
	g.Stop()
}

func CommonGmetrics(gmetric MetricSender) {
	gmetric("goroutines", fmt.Sprintf("%d", runtime.NumGoroutine()), Uint, "num", false)

	var mem runtime.MemStats
	runtime.ReadMemStats(&mem)
	gmetric("mem_alloc", fmt.Sprintf("%d", mem.Alloc), Uint, "bytes", false)
	gmetric("mem_sys", fmt.Sprintf("%d", mem.Sys), Uint, "bytes", false)
	gmetric("mem_gc_pause_last", fmt.Sprintf("%.6f", float64(mem.PauseNs[(mem.NumGC+255)%256])/1e6), Float, "ms", false)
	var gcPauseMax uint64
	for _, v := range mem.PauseNs {
		if v > gcPauseMax {
			gcPauseMax = v
		}
	}
	gmetric("mem_gc_pause_max", fmt.Sprintf("%.6f", float64(gcPauseMax)/1e6), Float, "ms", false)
	gmetric("mem_gc_pause_total", fmt.Sprintf("%.6f", float64(mem.PauseTotalNs)/1e6), Float, "ms", true)
	since := time.Now().Sub(time.Unix(0, int64(mem.LastGC))).Seconds()
	gmetric("mem_gc_pause_since", fmt.Sprintf("%.6f", since), Float, "sec", false)

	var r syscall.Rusage
	if syscall.Getrusage(syscall.RUSAGE_SELF, &r) == nil {
		gmetric("rusage_utime", fmt.Sprintf("%.6f", float64(r.Utime.Nano())/1e9), Float, "cpusecs", true)
		gmetric("rusage_stime", fmt.Sprintf("%.6f", float64(r.Stime.Nano())/1e9), Float, "cpusecs", true)
	}
}
<|endoftext|>


<commit_before>package geo_skeleton

import (
	"fmt"
	"log"
	"math"
	"os"
	"time"
)

import (
	"github.com/paulmach/go.geojson"
	"github.com/sjsafranek/DiffDB/diff_store"
	"github.com/sjsafranek/SkeletonDB"
)

var (
	COMMIT_LOG_FILE string = "geo_skeleton_commit.log"
)

const (
	DEFAULT_PRECISION        int = 8
)

// https://gist.github.com/DavidVaini/10308388
func Round(f float64) float64 {
	return math.Floor(f + .5)
}

func RoundToPrecision(f float64, places int) float64 {
	shift := math.Pow(10, float64(places))
	return Round(f*shift) / shift
}

func NewGeoSkeletonDB(db_file string) Database {
	var geoDb = Database{
		File:  db_file,
		Table: "GeoJsonDatasources",
		DB:    skeleton.Database{File: db_file}}
	geoDb.Init()
	return geoDb
}

// Database strust for application.
type Database struct {
	Table            string
	File             string
	commit_log_queue chan string
	Precision        int
	DB              skeleton.Database
}

func (self Database) Init() {

	self.DB.Init()

	// start commit log
	go self.StartCommitLog()

	// default table
	if "" == self.Table {
		self.Table = "GeoJSONLayers"
	}

	conn := self.DB.Connect()
	defer conn.Close()

	err := self.DB.CreateTable(conn, self.Table)
	if nil != err {
		panic(err)
	}

	err = self.DB.CreateTable(conn, "GeoTimeseriesData")
	if nil != err {
		panic(err)
	}
}

func (self Database) getPrecision() int {
	if 1 > self.Precision {
		return DEFAULT_PRECISION
	}
	return self.Precision
}

// Starts Database commit log
func (self *Database) StartCommitLog() {
	self.commit_log_queue = make(chan string, 10000)
	// open file to write database commit log
	COMMIT_LOG, err := os.OpenFile(COMMIT_LOG_FILE, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
	if err != nil {
		log.Println(err)
	}
	defer COMMIT_LOG.Close()
	// read from chan and write to file
	for {
		if len(self.commit_log_queue) > 0 {
			line := <-self.commit_log_queue
			if _, err := COMMIT_LOG.WriteString(line + "\n"); err != nil {
				panic(err)
			}
		} else {
			time.Sleep(1000 * time.Millisecond)
		}
	}
}

// CommitQueueLength returns length of database commit_log_queue
// @returns int
func (self *Database) CommitQueueLength() int {
	return len(self.commit_log_queue)
}

// NewLayer creates new datasource layer
// @returns string - datasource id
// @returns Error
// TODO: RENAME TO NewDatasource
func (self *Database) NewLayer() (string, error) {
	// create geojson
	datasource_id, _ := NewUUID()
	geojs := geojson.NewFeatureCollection()
	// convert to bytes
	value, err := geojs.MarshalJSON()
	if err != nil {
		return "", nil
	}
	self.commit_log_queue <- `{"method": "create_datasource", "data": { "datasource": "` + datasource_id + `", "layer": ` + string(value) + `}}`
	// Insert layer into database
	err = self.DB.Insert(self.Table, datasource_id, value)
	if err != nil {
		panic(err)
	}
	return datasource_id, err
}

// InsertLayer inserts layer into database
// @param datasource {string}
// @param geojs {Geojson}
// @returns Error
func (self *Database) InsertLayer(datasource_id string, geojs *geojson.FeatureCollection) error {
	// convert to bytes
	value, err := geojs.MarshalJSON()
	if err != nil {
		return err
	}

	err = self.DB.Insert(self.Table, datasource_id, value)
	if err != nil {
		panic(err)
	}

	go self.UpdateTimeseriesDatasource(datasource_id, value)

	return err
}

// GetLayer returns layer from database
// @param datasource {string}
// @returns Geojson
// @returns Error
func (self *Database) GetLayer(datasource_id string) (*geojson.FeatureCollection, error) {
	val, err := self.DB.Select(self.Table, datasource_id)
	if err != nil {
		return nil, err
	}
	if "" == string(val) {
		return nil, fmt.Errorf("Datasource not found")
	}
	// Read to struct
	geojs, err := geojson.UnmarshalFeatureCollection(val)
	if err != nil {
		return geojs, err
	}
	return geojs, nil
}

// GetLayers returns all datasource_ids from database
// @param datasource {string}
// @returns Geojson
// @returns Error
func (self *Database) GetLayers() ([]string, error) {
	val, err := self.DB.SelectAll(self.Table)
	if err != nil {
		return nil, err
	}
	return val, nil
}

// DeleteLayer deletes layer from database
// @param datasource {string}
// @returns Error
func (self *Database) DeleteLayer(datasource_id string) error {
	self.commit_log_queue <- `{"method": "delete_layer", "data": { "datasource": "` + datasource_id + `"}}`
	err := self.DB.Remove(datasource_id, self.Table)
	return err
}

func (self *Database) normalizeGeometry(feat *geojson.Feature) (*geojson.Feature, error) {
	// FIT TO 7 - 8 DECIMAL PLACES OF PRECISION
	if nil == feat.Geometry {
		return nil, fmt.Errorf("Feature has no geometry!")
	}

	precision := self.getPrecision()

	switch feat.Geometry.Type {

	case geojson.GeometryPoint:
		// []float64
		feat.Geometry.Point[0] = RoundToPrecision(feat.Geometry.Point[0], precision)
		feat.Geometry.Point[1] = RoundToPrecision(feat.Geometry.Point[1], precision)

	case geojson.GeometryMultiPoint:
		// [][]float64
		for i := range feat.Geometry.MultiPoint {
			for j := range feat.Geometry.MultiPoint[i] {
				feat.Geometry.MultiPoint[i][j] = RoundToPrecision(feat.Geometry.MultiPoint[i][j], precision)
			}
		}

	case geojson.GeometryLineString:
		// [][]float64
		for i := range feat.Geometry.LineString {
			for j := range feat.Geometry.LineString[i] {
				feat.Geometry.LineString[i][j] = RoundToPrecision(feat.Geometry.LineString[i][j], precision)
			}
		}

	case geojson.GeometryMultiLineString:
		// [][][]float64
		for i := range feat.Geometry.MultiLineString {
			for j := range feat.Geometry.MultiLineString[i] {
				for k := range feat.Geometry.MultiLineString[i][j] {
					feat.Geometry.MultiLineString[i][j][k] = RoundToPrecision(feat.Geometry.MultiLineString[i][j][k], precision)
				}
			}
		}

	case geojson.GeometryPolygon:
		// [][][]float64
		for i := range feat.Geometry.Polygon {
			for j := range feat.Geometry.Polygon[i] {
				for k := range feat.Geometry.Polygon[i][j] {
					feat.Geometry.Polygon[i][j][k] = RoundToPrecision(feat.Geometry.Polygon[i][j][k], precision)
				}
			}
		}

	case geojson.GeometryMultiPolygon:
		// [][][][]float64
		for i := range feat.Geometry.MultiPolygon {
			log.Printf("%v\n", feat.Geometry.MultiPolygon[i])
		}

	}

	/*
		//case GeometryCollection:
		//	geo.Geometries = g.Geometries
		//	// log.Printf("%v\n", feat.Geometry.Geometries)

	*/
	return feat, nil
}

func (self *Database) normalizeProperties(feat *geojson.Feature, featCollection *geojson.FeatureCollection) *geojson.Feature {

	// check if nil map
	if nil == feat.Properties {
		feat.Properties = make(map[string]interface{})
	}

	if 0 == len(featCollection.Features) {
		return feat
	}
	// Standardize properties for new feature
	for j := range featCollection.Features[0].Properties {
		if _, ok := feat.Properties[j]; !ok {
			feat.Properties[j] = ""
		}
	}

	// Standardize properties for existing features
	for i := range featCollection.Features {
		for j := range feat.Properties {
			if _, ok := featCollection.Features[i].Properties[j]; !ok {
				featCollection.Features[i].Properties[j] = ""
			}
		}
	}

	return feat
}

// InsertFeature adds feature to layer. Updates layer in Database
// @param datasource {string}
// @param feat {Geojson Feature}
// @returns Error
func (self *Database) InsertFeature(datasource_id string, feat *geojson.Feature) error {

	if nil == feat {
		return fmt.Errorf("feature value is <nil>!")
	}

	// Get layer from database
	featCollection, err := self.GetLayer(datasource_id)
	if err != nil {
		return err
	}

	// Apply required columns
	now := time.Now().Unix()

	// check if nil map
	if nil == feat.Properties {
		feat.Properties = make(map[string]interface{})
	}

	feat.Properties["is_active"] = true
	feat.Properties["is_deleted"] = false
	feat.Properties["date_created"] = now
	feat.Properties["date_modified"] = now
	feat.Properties["geo_id"] = fmt.Sprintf("%v", now)

	feat, err = self.normalizeGeometry(feat)
	if nil != err {
		return err
	}

	feat = self.normalizeProperties(feat, featCollection)

	// Write to commit log
	value, err := feat.MarshalJSON()
	if err != nil {
		return err
	}
	self.commit_log_queue <- `{"method": "insert_feature", "data": { "datasource": "` + datasource_id + `", "feature": ` + string(value) + `}}`

	// Add new feature to layer
	featCollection.AddFeature(feat)

	// insert layer
	err = self.InsertLayer(datasource_id, featCollection)
	if err != nil {
		panic(err)
	}
	return err
}

// EditFeature Edits feature in layer. Updates layer in Database
// @param datasource {string}
// @param geo_id {string}
// @param feat {Geojson Feature}
// @returns Error
func (self *Database) EditFeature(datasource_id string, geo_id string, feat *geojson.Feature) error {

	// Get layer from database
	featCollection, err := self.GetLayer(datasource_id)
	if err != nil {
		return err
	}

	feature_exists := false

	for i := range featCollection.Features {
		if geo_id == fmt.Sprintf("%v", featCollection.Features[i].Properties["geo_id"]) {

			now := time.Now().Unix()
			feat.Properties["date_modified"] = now

			feat, err = self.normalizeGeometry(feat)
			if nil != err {
				return err
			}

			feat = self.normalizeProperties(feat, featCollection)
			featCollection.Features[i] = feat
			// Write to commit log
			value, err := feat.MarshalJSON()
			if err != nil {
				return err
			}
			self.commit_log_queue <- `{"method": "edit_feature", "data": { "datasource": "` + datasource_id + `", "geo_id": "` + geo_id + `", "feature": ` + string(value) + `}}`
			feature_exists = true
		}
	}

	if !feature_exists {
		return fmt.Errorf("feature not found!")
	}

	// insert layer
	err = self.InsertLayer(datasource_id, featCollection)
	if err != nil {
		panic(err)
	}
	return err
}

func (self *Database) InsertTimeseriesDatasource(datasource_id string, enc []byte) (error) {
	err := self.DB.Insert("GeoTimeseriesData", datasource_id, enc)
	return err
}

func (self *Database) SelectTimeseriesDatasource(datasource_id string) ([]byte, error) {
	data, err := self.DB.Select("GeoTimeseriesData", datasource_id)
	return data, err
}

func (self *Database) UpdateTimeseriesDatasource(datasource_id string, value []byte) error {

	update_value := string(value)
	var ddata diff_store.DiffStore
	data, err := self.SelectTimeseriesDatasource(datasource_id)
	if nil != err {
		if err.Error() == "Not found" {
			// create new diffstore if key not found in database
			ddata = diff_store.NewDiffStore(datasource_id)
		} else {
			panic(err)
		}
	} else {
		ddata.Decode(data)
	}

	// update diffstore
	ddata.Update(update_value)

	// save to database
	enc, err := ddata.Encode()
	if nil != err {
		panic(err)
	}

	ddata.Name = datasource_id
	err = self.InsertTimeseriesDatasource(string(ddata.Name), enc)

	return err
}<commit_msg>changed select timeseries datasource<commit_after>package geo_skeleton

import (
	"fmt"
	"log"
	"math"
	"os"
	"time"
)

import (
	"github.com/paulmach/go.geojson"
	"github.com/sjsafranek/DiffDB/diff_store"
	"github.com/sjsafranek/SkeletonDB"
)

var (
	COMMIT_LOG_FILE string = "geo_skeleton_commit.log"
)

const (
	DEFAULT_PRECISION        int = 8
)

// https://gist.github.com/DavidVaini/10308388
func Round(f float64) float64 {
	return math.Floor(f + .5)
}

func RoundToPrecision(f float64, places int) float64 {
	shift := math.Pow(10, float64(places))
	return Round(f*shift) / shift
}

func NewGeoSkeletonDB(db_file string) Database {
	var geoDb = Database{
		File:  db_file,
		Table: "GeoJsonDatasources",
		DB:    skeleton.Database{File: db_file}}
	geoDb.Init()
	return geoDb
}

// Database strust for application.
type Database struct {
	Table            string
	File             string
	commit_log_queue chan string
	Precision        int
	DB              skeleton.Database
}

func (self Database) Init() {

	self.DB.Init()

	// start commit log
	go self.StartCommitLog()

	// default table
	if "" == self.Table {
		self.Table = "GeoJSONLayers"
	}

	conn := self.DB.Connect()
	defer conn.Close()

	err := self.DB.CreateTable(conn, self.Table)
	if nil != err {
		panic(err)
	}

	err = self.DB.CreateTable(conn, "GeoTimeseriesData")
	if nil != err {
		panic(err)
	}
}

func (self Database) getPrecision() int {
	if 1 > self.Precision {
		return DEFAULT_PRECISION
	}
	return self.Precision
}

// Starts Database commit log
func (self *Database) StartCommitLog() {
	self.commit_log_queue = make(chan string, 10000)
	// open file to write database commit log
	COMMIT_LOG, err := os.OpenFile(COMMIT_LOG_FILE, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
	if err != nil {
		log.Println(err)
	}
	defer COMMIT_LOG.Close()
	// read from chan and write to file
	for {
		if len(self.commit_log_queue) > 0 {
			line := <-self.commit_log_queue
			if _, err := COMMIT_LOG.WriteString(line + "\n"); err != nil {
				panic(err)
			}
		} else {
			time.Sleep(1000 * time.Millisecond)
		}
	}
}

// CommitQueueLength returns length of database commit_log_queue
// @returns int
func (self *Database) CommitQueueLength() int {
	return len(self.commit_log_queue)
}

// NewLayer creates new datasource layer
// @returns string - datasource id
// @returns Error
// TODO: RENAME TO NewDatasource
func (self *Database) NewLayer() (string, error) {
	// create geojson
	datasource_id, _ := NewUUID()
	geojs := geojson.NewFeatureCollection()
	// convert to bytes
	value, err := geojs.MarshalJSON()
	if err != nil {
		return "", nil
	}
	self.commit_log_queue <- `{"method": "create_datasource", "data": { "datasource": "` + datasource_id + `", "layer": ` + string(value) + `}}`
	// Insert layer into database
	err = self.DB.Insert(self.Table, datasource_id, value)
	if err != nil {
		panic(err)
	}
	return datasource_id, err
}

// InsertLayer inserts layer into database
// @param datasource {string}
// @param geojs {Geojson}
// @returns Error
func (self *Database) InsertLayer(datasource_id string, geojs *geojson.FeatureCollection) error {
	// convert to bytes
	value, err := geojs.MarshalJSON()
	if err != nil {
		return err
	}

	err = self.DB.Insert(self.Table, datasource_id, value)
	if err != nil {
		panic(err)
	}

	go self.UpdateTimeseriesDatasource(datasource_id, value)

	return err
}

// GetLayer returns layer from database
// @param datasource {string}
// @returns Geojson
// @returns Error
func (self *Database) GetLayer(datasource_id string) (*geojson.FeatureCollection, error) {
	val, err := self.DB.Select(self.Table, datasource_id)
	if err != nil {
		return nil, err
	}
	if "" == string(val) {
		return nil, fmt.Errorf("Datasource not found")
	}
	// Read to struct
	geojs, err := geojson.UnmarshalFeatureCollection(val)
	if err != nil {
		return geojs, err
	}
	return geojs, nil
}

// GetLayers returns all datasource_ids from database
// @param datasource {string}
// @returns Geojson
// @returns Error
func (self *Database) GetLayers() ([]string, error) {
	val, err := self.DB.SelectAll(self.Table)
	if err != nil {
		return nil, err
	}
	return val, nil
}

// DeleteLayer deletes layer from database
// @param datasource {string}
// @returns Error
func (self *Database) DeleteLayer(datasource_id string) error {
	self.commit_log_queue <- `{"method": "delete_layer", "data": { "datasource": "` + datasource_id + `"}}`
	err := self.DB.Remove(datasource_id, self.Table)
	return err
}

func (self *Database) normalizeGeometry(feat *geojson.Feature) (*geojson.Feature, error) {
	// FIT TO 7 - 8 DECIMAL PLACES OF PRECISION
	if nil == feat.Geometry {
		return nil, fmt.Errorf("Feature has no geometry!")
	}

	precision := self.getPrecision()

	switch feat.Geometry.Type {

	case geojson.GeometryPoint:
		// []float64
		feat.Geometry.Point[0] = RoundToPrecision(feat.Geometry.Point[0], precision)
		feat.Geometry.Point[1] = RoundToPrecision(feat.Geometry.Point[1], precision)

	case geojson.GeometryMultiPoint:
		// [][]float64
		for i := range feat.Geometry.MultiPoint {
			for j := range feat.Geometry.MultiPoint[i] {
				feat.Geometry.MultiPoint[i][j] = RoundToPrecision(feat.Geometry.MultiPoint[i][j], precision)
			}
		}

	case geojson.GeometryLineString:
		// [][]float64
		for i := range feat.Geometry.LineString {
			for j := range feat.Geometry.LineString[i] {
				feat.Geometry.LineString[i][j] = RoundToPrecision(feat.Geometry.LineString[i][j], precision)
			}
		}

	case geojson.GeometryMultiLineString:
		// [][][]float64
		for i := range feat.Geometry.MultiLineString {
			for j := range feat.Geometry.MultiLineString[i] {
				for k := range feat.Geometry.MultiLineString[i][j] {
					feat.Geometry.MultiLineString[i][j][k] = RoundToPrecision(feat.Geometry.MultiLineString[i][j][k], precision)
				}
			}
		}

	case geojson.GeometryPolygon:
		// [][][]float64
		for i := range feat.Geometry.Polygon {
			for j := range feat.Geometry.Polygon[i] {
				for k := range feat.Geometry.Polygon[i][j] {
					feat.Geometry.Polygon[i][j][k] = RoundToPrecision(feat.Geometry.Polygon[i][j][k], precision)
				}
			}
		}

	case geojson.GeometryMultiPolygon:
		// [][][][]float64
		for i := range feat.Geometry.MultiPolygon {
			log.Printf("%v\n", feat.Geometry.MultiPolygon[i])
		}

	}

	/*
		//case GeometryCollection:
		//	geo.Geometries = g.Geometries
		//	// log.Printf("%v\n", feat.Geometry.Geometries)

	*/
	return feat, nil
}

func (self *Database) normalizeProperties(feat *geojson.Feature, featCollection *geojson.FeatureCollection) *geojson.Feature {

	// check if nil map
	if nil == feat.Properties {
		feat.Properties = make(map[string]interface{})
	}

	if 0 == len(featCollection.Features) {
		return feat
	}
	// Standardize properties for new feature
	for j := range featCollection.Features[0].Properties {
		if _, ok := feat.Properties[j]; !ok {
			feat.Properties[j] = ""
		}
	}

	// Standardize properties for existing features
	for i := range featCollection.Features {
		for j := range feat.Properties {
			if _, ok := featCollection.Features[i].Properties[j]; !ok {
				featCollection.Features[i].Properties[j] = ""
			}
		}
	}

	return feat
}

// InsertFeature adds feature to layer. Updates layer in Database
// @param datasource {string}
// @param feat {Geojson Feature}
// @returns Error
func (self *Database) InsertFeature(datasource_id string, feat *geojson.Feature) error {

	if nil == feat {
		return fmt.Errorf("feature value is <nil>!")
	}

	// Get layer from database
	featCollection, err := self.GetLayer(datasource_id)
	if err != nil {
		return err
	}

	// Apply required columns
	now := time.Now().Unix()

	// check if nil map
	if nil == feat.Properties {
		feat.Properties = make(map[string]interface{})
	}

	feat.Properties["is_active"] = true
	feat.Properties["is_deleted"] = false
	feat.Properties["date_created"] = now
	feat.Properties["date_modified"] = now
	feat.Properties["geo_id"] = fmt.Sprintf("%v", now)

	feat, err = self.normalizeGeometry(feat)
	if nil != err {
		return err
	}

	feat = self.normalizeProperties(feat, featCollection)

	// Write to commit log
	value, err := feat.MarshalJSON()
	if err != nil {
		return err
	}
	self.commit_log_queue <- `{"method": "insert_feature", "data": { "datasource": "` + datasource_id + `", "feature": ` + string(value) + `}}`

	// Add new feature to layer
	featCollection.AddFeature(feat)

	// insert layer
	err = self.InsertLayer(datasource_id, featCollection)
	if err != nil {
		panic(err)
	}
	return err
}

// EditFeature Edits feature in layer. Updates layer in Database
// @param datasource {string}
// @param geo_id {string}
// @param feat {Geojson Feature}
// @returns Error
func (self *Database) EditFeature(datasource_id string, geo_id string, feat *geojson.Feature) error {

	// Get layer from database
	featCollection, err := self.GetLayer(datasource_id)
	if err != nil {
		return err
	}

	feature_exists := false

	for i := range featCollection.Features {
		if geo_id == fmt.Sprintf("%v", featCollection.Features[i].Properties["geo_id"]) {

			now := time.Now().Unix()
			feat.Properties["date_modified"] = now

			feat, err = self.normalizeGeometry(feat)
			if nil != err {
				return err
			}

			feat = self.normalizeProperties(feat, featCollection)
			featCollection.Features[i] = feat
			// Write to commit log
			value, err := feat.MarshalJSON()
			if err != nil {
				return err
			}
			self.commit_log_queue <- `{"method": "edit_feature", "data": { "datasource": "` + datasource_id + `", "geo_id": "` + geo_id + `", "feature": ` + string(value) + `}}`
			feature_exists = true
		}
	}

	if !feature_exists {
		return fmt.Errorf("feature not found!")
	}

	// insert layer
	err = self.InsertLayer(datasource_id, featCollection)
	if err != nil {
		panic(err)
	}
	return err
}

func (self *Database) InsertTimeseriesDatasource(datasource_id string, enc []byte) (error) {
	err := self.DB.Insert("GeoTimeseriesData", datasource_id, enc)
	return err
}

func (self *Database) SelectTimeseriesDatasource(datasource_id string) (diff_store.DiffStore, error) {
	var ddata diff_store.DiffStore
	data, err := self.DB.Select("GeoTimeseriesData", datasource_id)
	ddata.Decode(data)
	return data, err
}

func (self *Database) UpdateTimeseriesDatasource(datasource_id string, value []byte) error {

	update_value := string(value)

	//var ddata diff_store.DiffStore
	ddata, err := self.SelectTimeseriesDatasource(datasource_id)
	if nil != err {
		if err.Error() == "Not found" {
			// create new diffstore if key not found in database
			ddata = diff_store.NewDiffStore(datasource_id)
		} else {
			panic(err)
		}
	}

	// update diffstore
	ddata.Update(update_value)

	// save to database
	enc, err := ddata.Encode()
	if nil != err {
		panic(err)
	}

	ddata.Name = datasource_id
	err = self.InsertTimeseriesDatasource(string(ddata.Name), enc)

	return err
}<|endoftext|>


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

package gettext

import (
	"code.google.com/p/gorilla/gettext/pluralforms"
	"encoding/binary"
	"errors"
	"io"
	"strings"
)

const (
	magicBigEndian    = 0xde120495
	magicLittleEndian = 0x950412de
)

// Reader wraps the interfaces used to read compiled catalogs.
//
// Typically catalogs are provided as os.File.
type Reader interface {
	io.Reader
	io.ReaderAt
	io.Seeker
}

// ContextFunc is used to select the context stored for message disambiguation.
type ContextFunc func(string) bool

// NewCatalog returns a new Catalog, initializing its internal fields.
func NewCatalog() *Catalog {
	return &Catalog{
		PluralFunc: pluralforms.DefaultPluralFunc,
		info:       make(map[string]string),
		messages:   make(map[string]string),
		mPlurals:   make(map[string][]string),
		tPlurals:   make(map[string][]string),
	}
}

// Catalog stores gettext translations.
//
// Inspired by Python's gettext.GNUTranslations.
type Catalog struct {
	Fallback    *Catalog               // used when a translation is not found
	ContextFunc ContextFunc            // used to select context to load
	PluralFunc  pluralforms.PluralFunc // used to select the plural form index
	info        map[string]string      // metadata from file header
	messages    map[string]string      // original messages
	mPlurals    map[string][]string    // message plurals
	tPlurals    map[string][]string    // translation plurals
}

// Gettext returns a translation for the given message.
func (c *Catalog) Gettext(msg string) string {
	if trans, ok := c.messages[msg]; ok {
		return trans
	}
	if c.Fallback != nil {
		return c.Fallback.Gettext(msg)
	}
	return msg
}

// Ngettext returns a plural translation for a message according to the
// amount n.
//
// msg1 is used to lookup for a translation, and msg2 is used as the plural
// form fallback if a translation is not found.
func (c *Catalog) Ngettext(msg1, msg2 string, n int) string {
	if plurals, ok := c.tPlurals[msg1]; ok && c.PluralFunc != nil {
		if idx := c.PluralFunc(n); idx >= 0 && idx < len(plurals) {
			return plurals[idx]
		}
	}
	if c.Fallback != nil {
		return c.Fallback.Ngettext(msg1, msg2, n)
	}
	if n == 1 {
		return msg1
	}
	return msg2
}

// ReadMO reads a GNU MO file and writes its messages and translations
// to the catalog.
//
// GNU MO file format specification:
//
//     http://www.gnu.org/software/gettext/manual/gettext.html#MO-Files
//
// TODO: check if the format version is supported
func (c *Catalog) ReadMO(r Reader) error {
	// First word identifies the byte order.
	var order binary.ByteOrder
	var magic uint32
	if err := binary.Read(r, binary.LittleEndian, &magic); err != nil {
		return err
	}
	if magic == magicLittleEndian {
		order = binary.LittleEndian
	} else if magic == magicBigEndian {
		order = binary.BigEndian
	} else {
		return errors.New("Unable to identify the file byte order")
	}
	// Next six words:
	// - major+minor format version numbers (ignored)
	// - number of messages
	// - index of messages table
	// - index of translations table
	// - size of hashing table (ignored)
	// - offset of hashing table (ignored)
	w := make([]uint32, 6)
	for i, _ := range w {
		if err := binary.Read(r, order, &w[i]); err != nil {
			return err
		}
	}
	count, mTableIdx, tTableIdx := w[1], w[2], w[3]
	// Build a translations table of strings and translations.
	// Plurals are stored separately with the first message as key.
	var mLen, mIdx, tLen, tIdx uint32
	for i := 0; i < int(count); i++ {
		// Get original message length and position.
		r.Seek(int64(mTableIdx), 0)
		if err := binary.Read(r, order, &mLen); err != nil {
			return err
		}
		if err := binary.Read(r, order, &mIdx); err != nil {
			return err
		}
		// Get original message.
		m := make([]byte, mLen)
		if _, err := r.ReadAt(m, int64(mIdx)); err != nil {
			return err
		}
		// Get translation length and position.
		r.Seek(int64(tTableIdx), 0)
		if err := binary.Read(r, order, &tLen); err != nil {
			return err
		}
		if err := binary.Read(r, order, &tIdx); err != nil {
			return err
		}
		// Get translation.
		t := make([]byte, tLen)
		if _, err := r.ReadAt(t, int64(tIdx)); err != nil {
			return err
		}
		// Move cursor to next string.
		mTableIdx += 8
		tTableIdx += 8
		mStr, tStr := string(m), string(t)
		if mStr == "" {
			// This is the file header. Parse it.
			c.parseMOHeader(tStr)
			continue
		}
		// Check for context.
		if cIdx := strings.Index(mStr, "\x04"); cIdx != -1 {
			ctx := mStr[:cIdx]
			mStr = mStr[cIdx+1:]
			if c.ContextFunc != nil && !c.ContextFunc(ctx) {
				// Context is not valid.
				continue
			}
		}
		// Check for plurals.
		if pIdx := strings.Index(mStr, "\x00"); pIdx != -1 {
			// Store only the first original string and translation in the
			// messages map, and all versions in the two other maps.
			mPlurals := strings.Split(mStr, "\x00")
			tPlurals := strings.Split(tStr, "\x00")
			mStr = mPlurals[0]
			c.messages[mStr] = tPlurals[0]
			c.mPlurals[mStr] = mPlurals
			c.tPlurals[mStr] = tPlurals
		} else {
			c.messages[mStr] = tStr
		}
	}
	return nil
}

// parseMOHeader parses the catalog metadata following GNU .mo conventions.
//
// Ported from Python's gettext.GNUTranslations.
func (c *Catalog) parseMOHeader(str string) {
	var lastk string
	for _, item := range strings.Split(str, "\n") {
		item = strings.TrimSpace(item)
		if item == "" {
			continue
		}
		if i := strings.Index(item, ":"); i != -1 {
			k := strings.ToLower(strings.TrimSpace(item[:i]))
			v := strings.TrimSpace(item[i+1:])
			c.info[k] = v
			lastk = k
			switch k {
			// TODO: extract charset from content-type?
			case "plural-forms":
			L1:
				for _, part := range strings.Split(v, ";") {
					kv := strings.SplitN(part, "=", 2)
					if len(kv) == 2 && strings.TrimSpace(kv[0]) == "plural" {
						if fn, err := pluralforms.Parse(kv[1]); err == nil {
							c.PluralFunc = fn
						}
						break L1
					}
				}
			}
		} else if lastk != "" {
			c.info[lastk] += "\n" + item
		}
	}
}
<commit_msg>gettext: Added Gettextf()/Ngettextf(). Added support for expanding strings reordering arguments.<commit_after>// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gettext

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"

	"code.google.com/p/gorilla/gettext/pluralforms"
)

const (
	magicBigEndian    = 0xde120495
	magicLittleEndian = 0x950412de
)

// Reader wraps the interfaces used to read compiled catalogs.
//
// Typically catalogs are provided as os.File.
type Reader interface {
	io.Reader
	io.ReaderAt
	io.Seeker
}

// ContextFunc is used to select the context stored for message disambiguation.
type ContextFunc func(string) bool

// NewCatalog returns a new Catalog, initializing its internal fields.
func NewCatalog() *Catalog {
	return &Catalog{
		PluralFunc: pluralforms.DefaultPluralFunc,
		info:       make(map[string]string),
		messages:   make(map[string]string),
		mPlurals:   make(map[string][]string),
		tPlurals:   make(map[string][]string),
		tOrders:    make(map[string][][]int),
	}
}

// Catalog stores gettext translations.
type Catalog struct {
	Fallback    *Catalog               // used when a translation is not found
	ContextFunc ContextFunc            // used to select context to load
	PluralFunc  pluralforms.PluralFunc // used to select the plural form index
	info        map[string]string      // metadata from file header
	messages    map[string]string      // original messages
	mPlurals    map[string][]string    // message plurals
	tPlurals    map[string][]string    // translation plurals
	tOrders     map[string][][]int     // translation expansion orders
}

// Gettext returns a translation for the given message.
func (c *Catalog) Gettext(msg string) string {
	if trans, ok := c.messages[msg]; ok {
		return trans
	}
	if c.Fallback != nil {
		return c.Fallback.Gettext(msg)
	}
	return msg
}

// Gettextf returns a translation for the given message,
// formatted using fmt.Sprintf().
func (c *Catalog) Gettextf(msg string, a ...interface{}) string {
	if trans, ok := c.messages[msg]; ok {
		return sprintf(trans, c.tOrders[msg][0], a...)
	} else if c.Fallback != nil {
		return c.Fallback.Gettextf(msg, a...)
	}
	return fmt.Sprintf(msg, a...)
}

// Ngettext returns a plural translation for a message according to the
// amount n.
//
// msg1 is used to lookup for a translation, and msg2 is used as the plural
// form fallback if a translation is not found.
func (c *Catalog) Ngettext(msg1, msg2 string, n int) string {
	if plurals, ok := c.tPlurals[msg1]; ok && c.PluralFunc != nil {
		if idx := c.PluralFunc(n); idx >= 0 && idx < len(plurals) {
			return plurals[idx]
		}
	}
	if c.Fallback != nil {
		return c.Fallback.Ngettext(msg1, msg2, n)
	}
	if n == 1 {
		return msg1
	}
	return msg2
}

// Ngettextf returns a plural translation for the given message,
// formatted using fmt.Sprintf().
func (c *Catalog) Ngettextf(msg1, msg2 string, n int, a ...interface{}) string {
	if plurals, ok := c.tPlurals[msg1]; ok && c.PluralFunc != nil {
		if idx := c.PluralFunc(n); idx >= 0 && idx < len(plurals) {
			return sprintf(plurals[idx], c.tOrders[msg1][idx], a...)
		}
	}
	if c.Fallback != nil {
		return c.Fallback.Ngettextf(msg1, msg2, n, a...)
	}
	if n == 1 {
		return fmt.Sprintf(msg1, a...)
	}
	return fmt.Sprintf(msg2, a...)
}

// ReadMO reads a GNU MO file and writes its messages and translations
// to the catalog.
//
// GNU MO file format specification:
//
//     http://www.gnu.org/software/gettext/manual/gettext.html#MO-Files
//
// Inspired by Python's gettext.GNUTranslations.
//
// TODO: check if the format version is supported
//
// MO format revisions (to be confirmed):
// Major revision is 0 or 1. Minor revision is also 0 or 1.
//
// - Major revision 1: supports "I" flag for outdigits in string replacements,
//   e.g., translating "%d" to "%Id". The result is that ASCII digits are
//   replaced with the "outdigits" defined in the LC_CTYPE locale category.
//
// - Minor revision 1: supports reordering ability for string replacements,
//   e.g., using "%2$d" to indicate the position of the replacement.
func (c *Catalog) ReadMO(r Reader) error {
	// First word identifies the byte order.
	var order binary.ByteOrder
	var magic uint32
	if err := binary.Read(r, binary.LittleEndian, &magic); err != nil {
		return err
	}
	if magic == magicLittleEndian {
		order = binary.LittleEndian
	} else if magic == magicBigEndian {
		order = binary.BigEndian
	} else {
		return errors.New("Unable to identify the file byte order")
	}
	// Next six words:
	// - major+minor format version numbers (ignored)
	// - number of messages
	// - index of messages table
	// - index of translations table
	// - size of hashing table (ignored)
	// - offset of hashing table (ignored)
	w := make([]uint32, 6)
	for i, _ := range w {
		if err := binary.Read(r, order, &w[i]); err != nil {
			return err
		}
	}
	count, mTableIdx, tTableIdx := w[1], w[2], w[3]
	// Build a translations table of strings and translations.
	// Plurals are stored separately with the first message as key.
	var mLen, mIdx, tLen, tIdx uint32
	for i := 0; i < int(count); i++ {
		// Get original message length and position.
		r.Seek(int64(mTableIdx), 0)
		if err := binary.Read(r, order, &mLen); err != nil {
			return err
		}
		if err := binary.Read(r, order, &mIdx); err != nil {
			return err
		}
		// Get original message.
		m := make([]byte, mLen)
		if _, err := r.ReadAt(m, int64(mIdx)); err != nil {
			return err
		}
		// Get translation length and position.
		r.Seek(int64(tTableIdx), 0)
		if err := binary.Read(r, order, &tLen); err != nil {
			return err
		}
		if err := binary.Read(r, order, &tIdx); err != nil {
			return err
		}
		// Get translation.
		t := make([]byte, tLen)
		if _, err := r.ReadAt(t, int64(tIdx)); err != nil {
			return err
		}
		// Move cursor to next string.
		mTableIdx += 8
		tTableIdx += 8
		mStr, tStr := string(m), string(t)
		if mStr == "" {
			// This is the file header. Parse it.
			c.readMOHeader(tStr)
			continue
		}
		// Check for context.
		if cIdx := strings.Index(mStr, "\x04"); cIdx != -1 {
			ctx := mStr[:cIdx]
			mStr = mStr[cIdx+1:]
			if c.ContextFunc != nil && !c.ContextFunc(ctx) {
				// Context is not valid.
				continue
			}
		}
		// Check for plurals.
		if pIdx := strings.Index(mStr, "\x00"); pIdx != -1 {
			// Store only the first original string and translation in the
			// messages map, and all versions in the two other maps.
			mPlurals := strings.Split(mStr, "\x00")
			tPlurals := strings.Split(tStr, "\x00")
			mStr = mPlurals[0]
			c.messages[mStr] = tPlurals[0]
			c.mPlurals[mStr] = mPlurals
			for _, tPlural := range tPlurals {
				format, orders := parseFmt(tPlural)
				c.tPlurals[mStr] = append(c.tPlurals[mStr], format)
				c.tOrders[mStr] = append(c.tOrders[mStr], orders)
			}
		} else {
			format, orders := parseFmt(tStr)
			c.messages[mStr] = format
			c.tOrders[mStr] = append(c.tOrders[mStr], orders)
		}
	}
	return nil
}

// readMOHeader parses the catalog metadata following GNU .mo conventions.
//
// Ported from Python's gettext.GNUTranslations.
func (c *Catalog) readMOHeader(str string) {
	var lastk string
	for _, item := range strings.Split(str, "\n") {
		item = strings.TrimSpace(item)
		if item == "" {
			continue
		}
		if i := strings.Index(item, ":"); i != -1 {
			k := strings.ToLower(strings.TrimSpace(item[:i]))
			v := strings.TrimSpace(item[i+1:])
			c.info[k] = v
			lastk = k
			switch k {
			// TODO: extract charset from content-type?
			case "plural-forms":
			L1:
				for _, part := range strings.Split(v, ";") {
					kv := strings.SplitN(part, "=", 2)
					if len(kv) == 2 && strings.TrimSpace(kv[0]) == "plural" {
						if fn, err := pluralforms.Parse(kv[1]); err == nil {
							c.PluralFunc = fn
						}
						break L1
					}
				}
			}
		} else if lastk != "" {
			c.info[lastk] += "\n" + item
		}
	}
}

// ----------------------------------------------------------------------------

var fmtRegexp = regexp.MustCompile(`%\d+\$`)

// parseFmt converts a string that relies on reordering ability to a standard
// format, e.g., the string "%2$d bytes on %1$s." becomes "%d bytes on %s.".
// The returned indices are used to format the string using sprintf().
func parseFmt(format string) (string, []int) {
	matches := fmtRegexp.FindAllStringIndex(format, -1)
	if len(matches) == 0 {
		return format, nil
	}
	buf := new(bytes.Buffer)
	idx := make([]int, 0)
	var i int
	for _, v := range matches {
		i1, i2 := v[0], v[1]
		if i1 > 0 && format[i1-1] == '%' {
			// Ignore escaped sequence.
			buf.WriteString(format[i:i2])
		} else {
			buf.WriteString(format[i:i1+1])
			pos, _ := strconv.ParseInt(format[i1+1:i2-1], 10, 0)
			idx = append(idx, int(pos)-1)
		}
		i = i2
	}
	buf.WriteString(format[i:])
	return buf.String(), idx
}

// sprintf applies fmt.Sprintf() on a string that relies on reordering
// ability, e.g., for the string "%2$d bytes free on %1$s.", the order of
// arguments must be inverted.
func sprintf(format string, order []int, a ...interface{}) string {
	if len(order) == 0 {
		return fmt.Sprintf(format, a...)
	}
	b := make([]interface{}, len(order))
	l := len(a)
	for k, v := range order {
		if v < l {
			b[k] = a[v]
		}
	}
	return fmt.Sprintf(format, b...)
}
<|endoftext|>


<commit_before>package apigee

import (
	"fmt"
	"log"
	"strconv"
	"strings"

	"github.com/gofrs/uuid"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/zambien/go-apigee-edge"
)

func resourceTargetServer() *schema.Resource {
	return &schema.Resource{
		Create: resourceTargetServerCreate,
		Read:   resourceTargetServerRead,
		Update: resourceTargetServerUpdate,
		Delete: resourceTargetServerDelete,
		Importer: &schema.ResourceImporter{
			State: resourceTargetServerImport,
		},

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"host": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: false,
			},
			"env": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"enabled": {
				Type:     schema.TypeBool,
				Required: true,
			},
			"port": {
				Type:     schema.TypeString,
				Required: true,
			},
			"ssl_info": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				MaxItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"ssl_enabled": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},
						"client_auth_enabled": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},
						"key_store": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
						},
						"trust_store": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
						},
						"key_alias": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
						},
						"ciphers": &schema.Schema{
							Type:     schema.TypeList,
							Optional: true,
							Elem:     &schema.Schema{Type: schema.TypeString},
						},
						"ignore_validation_errors": &schema.Schema{
							Type:     schema.TypeBool,
							Required: true,
						},
						"protocols": &schema.Schema{
							Type:     schema.TypeList,
							Optional: true,
							Elem:     &schema.Schema{Type: schema.TypeString},
						},
					},
				},
			},
		},
	}
}

func resourceTargetServerCreate(d *schema.ResourceData, meta interface{}) error {

	log.Print("[DEBUG] resourceTargetServerCreate START")

	client := meta.(*apigee.EdgeClient)

	u1, _ := uuid.NewV4()
	d.SetId(u1.String())

	targetServerData, err := setTargetServerData(d)
	if err != nil {
		log.Printf("[ERROR] resourceTargetServerCreate error in setTargetServerData: %s", err.Error())
		return fmt.Errorf("[ERROR] resourceTargetServerCreate error in setTargetServerData: %s", err.Error())
	}

	_, _, e := client.TargetServers.Create(targetServerData, d.Get("env").(string))
	if e != nil {
		log.Printf("[ERROR] resourceTargetServerCreate error in create: %s", e.Error())
		return fmt.Errorf("[ERROR] resourceTargetServerCreate error in create: %s", e.Error())
	}

	return resourceTargetServerRead(d, meta)
}

func resourceTargetServerImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {

	log.Print("[DEBUG] resourceTargetServerImport START")
	client := meta.(*apigee.EdgeClient)
	if len(strings.Split(d.Id(), "_")) != 2 {
		return []*schema.ResourceData{}, fmt.Errorf("[ERR] Wrong format of resource: %s. Please follow '{name}_{env}'", d.Id())
	}
	name := strings.Split(d.Id(), "_")[0]
	IDEnv := strings.Split(d.Id(), "_")[1]
	targetServerData, _, err := client.TargetServers.Get(name, IDEnv)
	if err != nil {
		log.Printf("[ERROR] resourceTargetServerImport error getting target servers: %s", err.Error())
		if strings.Contains(err.Error(), "404 ") {
			return []*schema.ResourceData{}, fmt.Errorf("[Error] resourceTargetServerImport 404 encountered.  Removing state for target server: %#v", d.Get("name").(string))
		}
		return []*schema.ResourceData{}, fmt.Errorf("[ERROR] resourceTargetServerImport error getting target servers: %s", err.Error())
	}

	d.Set("name", targetServerData.Name)
	d.Set("host", targetServerData.Host)
	d.Set("enabled", targetServerData.Enabled)
	d.Set("port", targetServerData.Port)
	d.Set("env", IDEnv)

	protocols := flattenStringList(targetServerData.SSLInfo.Protocols)
	ciphers := flattenStringList(targetServerData.SSLInfo.Ciphers)

	d.Set("ssl_info.0.ssl_enabled", targetServerData.SSLInfo.SSLEnabled)
	d.Set("ssl_info.0.client_auth_enabled", targetServerData.SSLInfo.ClientAuthEnabled)
	d.Set("ssl_info.0.key_store", targetServerData.SSLInfo.KeyStore)
	d.Set("ssl_info.0.trust_store", targetServerData.SSLInfo.TrustStore)
	d.Set("ssl_info.0.key_alias", targetServerData.SSLInfo.KeyAlias)
	d.Set("ssl_info.0.ciphers", ciphers)
	d.Set("ssl_info.0.ignore_validation_errors", targetServerData.SSLInfo.IgnoreValidationErrors)
	d.Set("ssl_info.0.protocols", protocols)

	return []*schema.ResourceData{d}, nil
}

func resourceTargetServerRead(d *schema.ResourceData, meta interface{}) error {

	log.Print("[DEBUG] resourceTargetServerRead START")
	client := meta.(*apigee.EdgeClient)

	targetServerData, _, err := client.TargetServers.Get(d.Get("name").(string), d.Get("env").(string))
	if err != nil {
		log.Printf("[ERROR] resourceTargetServerRead error getting target servers: %s", err.Error())
		if strings.Contains(err.Error(), "404 ") {
			log.Printf("[DEBUG] resourceTargetServerRead 404 encountered.  Removing state for target server: %#v", d.Get("name").(string))
			d.SetId("")
			return nil
		} else {
			return fmt.Errorf("[ERROR] resourceTargetServerRead error getting target servers: %s", err.Error())
		}
	}

	d.Set("name", targetServerData.Name)
	d.Set("host", targetServerData.Host)
	d.Set("enabled", targetServerData.Enabled)
	d.Set("port", targetServerData.Port)

	protocols := flattenStringList(targetServerData.SSLInfo.Protocols)
	ciphers := flattenStringList(targetServerData.SSLInfo.Ciphers)

	d.Set("ssl_info.0.ssl_enabled", targetServerData.SSLInfo.SSLEnabled)
	d.Set("ssl_info.0.client_auth_enabled", targetServerData.SSLInfo.ClientAuthEnabled)
	d.Set("ssl_info.0.key_store", targetServerData.SSLInfo.KeyStore)
	d.Set("ssl_info.0.trust_store", targetServerData.SSLInfo.TrustStore)
	d.Set("ssl_info.0.key_alias", targetServerData.SSLInfo.KeyAlias)
	d.Set("ssl_info.0.ciphers", ciphers)
	d.Set("ssl_info.0.ignore_validation_errors", targetServerData.SSLInfo.IgnoreValidationErrors)
	d.Set("ssl_info.0.protocols", protocols)

	return nil
}

func resourceTargetServerUpdate(d *schema.ResourceData, meta interface{}) error {

	log.Print("[DEBUG] resourceTargetServerUpdate START")

	client := meta.(*apigee.EdgeClient)

	targetServerData, err := setTargetServerData(d)
	if err != nil {
		log.Printf("[ERROR] resourceTargetServerUpdate error in setTargetServerData: %s", err.Error())
		return fmt.Errorf("[ERROR] resourceTargetServerUpdate error in setTargetServerData: %s", err.Error())
	}

	_, _, e := client.TargetServers.Update(targetServerData, d.Get("env").(string))
	if e != nil {
		log.Printf("[ERROR] resourceTargetServerUpdate error in update: %s", e.Error())
		return fmt.Errorf("[ERROR] resourceTargetServerUpdate error in update: %s", e.Error())
	}

	return resourceTargetServerRead(d, meta)
}

func resourceTargetServerDelete(d *schema.ResourceData, meta interface{}) error {

	log.Print("[DEBUG] resourceTargetServerDelete START")
	client := meta.(*apigee.EdgeClient)

	_, err := client.TargetServers.Delete(d.Get("name").(string), d.Get("env").(string))
	if err != nil {
		log.Printf("[ERROR] resourceTargetServerDelete error in delete: %s", err.Error())
		return fmt.Errorf("[ERROR] resourceTargetServerDelete error in delete: %s", err.Error())
	}

	return nil
}

func setTargetServerData(d *schema.ResourceData) (apigee.TargetServer, error) {

	log.Print("[DEBUG] setTargetServerData START")

	port_int, _ := strconv.Atoi(d.Get("port").(string))

	ciphers := []string{""}
	if d.Get("ssl_info.0.ciphers") != nil {
		ciphers = getStringList("ssl_info.0.ciphers", d)
	}

	protocols := []string{""}
	if d.Get("ssl_info.0.protocols") != nil {
		protocols = getStringList("ssl_info.0.protocols", d)
	}

	targetServer := apigee.TargetServer{
		Name:    d.Get("name").(string),
		Host:    d.Get("host").(string),
		Enabled: d.Get("enabled").(bool),
		Port:    port_int,
		SSLInfo: apigee.SSLInfo{
			SSLEnabled:        d.Get("ssl_info.0.ssl_enabled").(string),
			ClientAuthEnabled: d.Get("ssl_info.0.client_auth_enabled").(string),
			KeyStore:          d.Get("ssl_info.0.key_store").(string),
			TrustStore:        d.Get("ssl_info.0.trust_store").(string),
			KeyAlias:          d.Get("ssl_info.0.key_alias").(string),
			Ciphers:           ciphers,
			//Ciphers: d.Get("ssl_info.0.ciphers").([]string),
			IgnoreValidationErrors: d.Get("ssl_info.0.ignore_validation_errors").(bool),
			Protocols:              protocols,
		},
	}

	return targetServer, nil
}
<commit_msg>updating err msg<commit_after>package apigee

import (
	"fmt"
	"log"
	"strconv"
	"strings"

	"github.com/gofrs/uuid"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/zambien/go-apigee-edge"
)

func resourceTargetServer() *schema.Resource {
	return &schema.Resource{
		Create: resourceTargetServerCreate,
		Read:   resourceTargetServerRead,
		Update: resourceTargetServerUpdate,
		Delete: resourceTargetServerDelete,
		Importer: &schema.ResourceImporter{
			State: resourceTargetServerImport,
		},

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"host": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: false,
			},
			"env": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"enabled": {
				Type:     schema.TypeBool,
				Required: true,
			},
			"port": {
				Type:     schema.TypeString,
				Required: true,
			},
			"ssl_info": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				MaxItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"ssl_enabled": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},
						"client_auth_enabled": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},
						"key_store": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
						},
						"trust_store": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
						},
						"key_alias": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
						},
						"ciphers": &schema.Schema{
							Type:     schema.TypeList,
							Optional: true,
							Elem:     &schema.Schema{Type: schema.TypeString},
						},
						"ignore_validation_errors": &schema.Schema{
							Type:     schema.TypeBool,
							Required: true,
						},
						"protocols": &schema.Schema{
							Type:     schema.TypeList,
							Optional: true,
							Elem:     &schema.Schema{Type: schema.TypeString},
						},
					},
				},
			},
		},
	}
}

func resourceTargetServerCreate(d *schema.ResourceData, meta interface{}) error {

	log.Print("[DEBUG] resourceTargetServerCreate START")

	client := meta.(*apigee.EdgeClient)

	u1, _ := uuid.NewV4()
	d.SetId(u1.String())

	targetServerData, err := setTargetServerData(d)
	if err != nil {
		log.Printf("[ERROR] resourceTargetServerCreate error in setTargetServerData: %s", err.Error())
		return fmt.Errorf("[ERROR] resourceTargetServerCreate error in setTargetServerData: %s", err.Error())
	}

	_, _, e := client.TargetServers.Create(targetServerData, d.Get("env").(string))
	if e != nil {
		log.Printf("[ERROR] resourceTargetServerCreate error in create: %s", e.Error())
		return fmt.Errorf("[ERROR] resourceTargetServerCreate error in create: %s", e.Error())
	}

	return resourceTargetServerRead(d, meta)
}

func resourceTargetServerImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {

	log.Print("[DEBUG] resourceTargetServerImport START")
	client := meta.(*apigee.EdgeClient)
	if len(strings.Split(d.Id(), "_")) != 2 {
		return []*schema.ResourceData{}, fmt.Errorf("[ERR] Wrong format of resource: %s. Please follow '{name}_{env}'", d.Id())
	}
	name := strings.Split(d.Id(), "_")[0]
	IDEnv := strings.Split(d.Id(), "_")[1]
	targetServerData, _, err := client.TargetServers.Get(name, IDEnv)
	if err != nil {
		log.Printf("[ERROR] resourceTargetServerImport error getting target servers: %s", err.Error())
		if strings.Contains(err.Error(), "404 ") {
			return []*schema.ResourceData{}, fmt.Errorf("[Error] resourceTargetServerImport 404 encountered.  Removing state for target server: %#v", name)
		}
		return []*schema.ResourceData{}, fmt.Errorf("[ERROR] resourceTargetServerImport error getting target servers: %s", err.Error())
	}

	d.Set("name", targetServerData.Name)
	d.Set("host", targetServerData.Host)
	d.Set("enabled", targetServerData.Enabled)
	d.Set("port", targetServerData.Port)
	d.Set("env", IDEnv)

	protocols := flattenStringList(targetServerData.SSLInfo.Protocols)
	ciphers := flattenStringList(targetServerData.SSLInfo.Ciphers)

	d.Set("ssl_info.0.ssl_enabled", targetServerData.SSLInfo.SSLEnabled)
	d.Set("ssl_info.0.client_auth_enabled", targetServerData.SSLInfo.ClientAuthEnabled)
	d.Set("ssl_info.0.key_store", targetServerData.SSLInfo.KeyStore)
	d.Set("ssl_info.0.trust_store", targetServerData.SSLInfo.TrustStore)
	d.Set("ssl_info.0.key_alias", targetServerData.SSLInfo.KeyAlias)
	d.Set("ssl_info.0.ciphers", ciphers)
	d.Set("ssl_info.0.ignore_validation_errors", targetServerData.SSLInfo.IgnoreValidationErrors)
	d.Set("ssl_info.0.protocols", protocols)

	return []*schema.ResourceData{d}, nil
}

func resourceTargetServerRead(d *schema.ResourceData, meta interface{}) error {

	log.Print("[DEBUG] resourceTargetServerRead START")
	client := meta.(*apigee.EdgeClient)

	targetServerData, _, err := client.TargetServers.Get(d.Get("name").(string), d.Get("env").(string))
	if err != nil {
		log.Printf("[ERROR] resourceTargetServerRead error getting target servers: %s", err.Error())
		if strings.Contains(err.Error(), "404 ") {
			log.Printf("[DEBUG] resourceTargetServerRead 404 encountered.  Removing state for target server: %#v", d.Get("name").(string))
			d.SetId("")
			return nil
		} else {
			return fmt.Errorf("[ERROR] resourceTargetServerRead error getting target servers: %s", err.Error())
		}
	}

	d.Set("name", targetServerData.Name)
	d.Set("host", targetServerData.Host)
	d.Set("enabled", targetServerData.Enabled)
	d.Set("port", targetServerData.Port)

	protocols := flattenStringList(targetServerData.SSLInfo.Protocols)
	ciphers := flattenStringList(targetServerData.SSLInfo.Ciphers)

	d.Set("ssl_info.0.ssl_enabled", targetServerData.SSLInfo.SSLEnabled)
	d.Set("ssl_info.0.client_auth_enabled", targetServerData.SSLInfo.ClientAuthEnabled)
	d.Set("ssl_info.0.key_store", targetServerData.SSLInfo.KeyStore)
	d.Set("ssl_info.0.trust_store", targetServerData.SSLInfo.TrustStore)
	d.Set("ssl_info.0.key_alias", targetServerData.SSLInfo.KeyAlias)
	d.Set("ssl_info.0.ciphers", ciphers)
	d.Set("ssl_info.0.ignore_validation_errors", targetServerData.SSLInfo.IgnoreValidationErrors)
	d.Set("ssl_info.0.protocols", protocols)

	return nil
}

func resourceTargetServerUpdate(d *schema.ResourceData, meta interface{}) error {

	log.Print("[DEBUG] resourceTargetServerUpdate START")

	client := meta.(*apigee.EdgeClient)

	targetServerData, err := setTargetServerData(d)
	if err != nil {
		log.Printf("[ERROR] resourceTargetServerUpdate error in setTargetServerData: %s", err.Error())
		return fmt.Errorf("[ERROR] resourceTargetServerUpdate error in setTargetServerData: %s", err.Error())
	}

	_, _, e := client.TargetServers.Update(targetServerData, d.Get("env").(string))
	if e != nil {
		log.Printf("[ERROR] resourceTargetServerUpdate error in update: %s", e.Error())
		return fmt.Errorf("[ERROR] resourceTargetServerUpdate error in update: %s", e.Error())
	}

	return resourceTargetServerRead(d, meta)
}

func resourceTargetServerDelete(d *schema.ResourceData, meta interface{}) error {

	log.Print("[DEBUG] resourceTargetServerDelete START")
	client := meta.(*apigee.EdgeClient)

	_, err := client.TargetServers.Delete(d.Get("name").(string), d.Get("env").(string))
	if err != nil {
		log.Printf("[ERROR] resourceTargetServerDelete error in delete: %s", err.Error())
		return fmt.Errorf("[ERROR] resourceTargetServerDelete error in delete: %s", err.Error())
	}

	return nil
}

func setTargetServerData(d *schema.ResourceData) (apigee.TargetServer, error) {

	log.Print("[DEBUG] setTargetServerData START")

	port_int, _ := strconv.Atoi(d.Get("port").(string))

	ciphers := []string{""}
	if d.Get("ssl_info.0.ciphers") != nil {
		ciphers = getStringList("ssl_info.0.ciphers", d)
	}

	protocols := []string{""}
	if d.Get("ssl_info.0.protocols") != nil {
		protocols = getStringList("ssl_info.0.protocols", d)
	}

	targetServer := apigee.TargetServer{
		Name:    d.Get("name").(string),
		Host:    d.Get("host").(string),
		Enabled: d.Get("enabled").(bool),
		Port:    port_int,
		SSLInfo: apigee.SSLInfo{
			SSLEnabled:        d.Get("ssl_info.0.ssl_enabled").(string),
			ClientAuthEnabled: d.Get("ssl_info.0.client_auth_enabled").(string),
			KeyStore:          d.Get("ssl_info.0.key_store").(string),
			TrustStore:        d.Get("ssl_info.0.trust_store").(string),
			KeyAlias:          d.Get("ssl_info.0.key_alias").(string),
			Ciphers:           ciphers,
			//Ciphers: d.Get("ssl_info.0.ciphers").([]string),
			IgnoreValidationErrors: d.Get("ssl_info.0.ignore_validation_errors").(bool),
			Protocols:              protocols,
		},
	}

	return targetServer, nil
}
<|endoftext|>