<commit_before>// Copyright ©2014 The gonum 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 optimize

import (
	"fmt"
	"math"
	"time"

	"github.com/gonum/floats"
	"github.com/gonum/matrix/mat64"
)

// Local finds a local minimum of a function using a sequential algorithm.
// In order to maximize a function, multiply the output by -1.
//
// The first argument is of Function type representing the function to be minimized.
// Type switching is used to see if the function implements Gradient, FunctionGradient
// and Statuser.
//
// The second argument is the initial location at which to start the minimization.
// The initial location must be supplied, and must have a length equal to the
// problem dimension.
//
// The third argument contains the settings for the minimization. It is here that
// gradient tolerance, etc. are specified. The DefaultSettings() function
// can be called for a Settings struct with the default values initialized.
// If settings == nil, the default settings are used. Please see the documentation
// for the Settings structure for more information. The optimization Method used
// may also contain settings, see documentation for the appropriate optimizer.
//
// The final argument is the optimization method to use. If method == nil, then
// an appropriate default is chosen based on the properties of the other arguments
// (dimension, gradient-free or gradient-based, etc.). The optimization
// methods in this package are designed such that reasonable defaults occur
// if options are not specified explicitly. For example, the code
//  method := &Bfgs{}
// creates a pointer to a new Bfgs struct. When minimize is called, the settings
// in the method will be populated with default values. The methods are also
// designed such that they can be reused in future calls to method.
//
// Local returns a Result struct and any error that occurred. Please see the
// documentation of Result for more information.
//
// Please be aware that the default behavior of Local is to find the minimum.
// For certain functions and optimization methods, this process can take many
// function evaluations. If you would like to put limits on this, for example
// maximum runtime or maximum function evaluations, please modify the Settings
// input struct.
func Local(f Function, initX []float64, settings *Settings, method Method) (*Result, error) {
	if len(initX) == 0 {
		panic("local: initial X has zero length")
	}

	startTime := time.Now()
	funcInfo := newFunctionInfo(f)
	if method == nil {
		method = getDefaultMethod(funcInfo)
	}
	if err := funcInfo.satisfies(method); err != nil {
		return nil, err
	}

	if funcInfo.IsStatuser {
		_, err := funcInfo.statuser.Status()
		if err != nil {
			return nil, err
		}
	}

	if settings == nil {
		settings = DefaultSettings()
	}

	if settings.Recorder != nil {
		// Initialize Recorder first. If it fails, we avoid the (possibly
		// time-consuming) evaluation of F and DF at the starting location.
		err := settings.Recorder.Init(&funcInfo.FunctionInfo)
		if err != nil {
			return nil, err
		}
	}

	stats := &Stats{}
	optLoc, evalType, err := getStartingLocation(funcInfo, method, initX, stats, settings)
	if err != nil {
		return nil, err
	}

	// Runtime is the only Stats field that needs to be updated here.
	stats.Runtime = time.Since(startTime)
	// Send optLoc to Recorder before checking it for convergence.
	if settings.Recorder != nil {
		err = settings.Recorder.Record(optLoc, evalType, InitIteration, stats)
	}

	// Check if the starting location satisfies the convergence criteria.
	status := checkConvergence(optLoc, InitIteration, stats, settings)
	if status == NotTerminated && err == nil {
		// The starting location is not good enough, we need to perform a
		// minimization. The optimal location will be stored in-place in
		// optLoc.
		status, err = minimize(settings, method, funcInfo, stats, optLoc, startTime)
	}

	if settings.Recorder != nil && err == nil {
		// Send the optimal location to Recorder.
		err = settings.Recorder.Record(optLoc, NoEvaluation, PostIteration, stats)
	}
	stats.Runtime = time.Since(startTime)
	return &Result{
		Location: *optLoc,
		Stats:    *stats,
		Status:   status,
	}, err
}

func minimize(settings *Settings, method Method, funcInfo *functionInfo, stats *Stats, optLoc *Location, startTime time.Time) (status Status, err error) {
	loc := &Location{}
	copyLocation(loc, optLoc)
	xNext := make([]float64, len(loc.X))

	methodStatus, methodIsStatuser := method.(Statuser)

	evalType, iterType, err := method.Init(loc, &funcInfo.FunctionInfo, xNext)
	if err != nil {
		return Failure, err
	}

	for {
		if funcInfo.IsStatuser {
			// Check the function status before evaluating.
			status, err = funcInfo.statuser.Status()
			if err != nil || status != NotTerminated {
				return
			}
		}

		// Perform evalType evaluation of the function at xNext and store the
		// result in location.
		evaluate(funcInfo, evalType, xNext, loc, stats)
		// Update the stats and optLoc.
		update(loc, optLoc, stats, iterType, startTime)
		// Get the convergence status before recording the new location.
		status = checkConvergence(optLoc, iterType, stats, settings)

		if settings.Recorder != nil {
			err = settings.Recorder.Record(loc, evalType, iterType, stats)
			if err != nil {
				if status == NotTerminated {
					status = Failure
				}
				return
			}
		}

		if status != NotTerminated {
			return
		}

		if methodIsStatuser {
			status, err = methodStatus.Status()
			if err != nil || status != NotTerminated {
				return
			}
		}

		// Find the next location (stored in-place into xNext).
		evalType, iterType, err = method.Iterate(loc, xNext)
		if err != nil {
			status = Failure
			return
		}
	}
	panic("unreachable")
}

func copyLocation(dst, src *Location) {
	dst.X = resize(dst.X, len(src.X))
	copy(dst.X, src.X)

	dst.F = src.F

	dst.Gradient = resize(dst.Gradient, len(src.Gradient))
	copy(dst.Gradient, src.Gradient)
}

func getDefaultMethod(funcInfo *functionInfo) Method {
	if funcInfo.IsGradient || funcInfo.IsFunctionGradient {
		return &BFGS{}
	}
	// TODO: Implement a gradient-free method
	panic("optimize: gradient-free methods not yet coded")
}

// getStartingLocation allocates and initializes the starting location for the minimization.
func getStartingLocation(funcInfo *functionInfo, method Method, initX []float64, stats *Stats, settings *Settings) (*Location, EvaluationType, error) {
	dim := len(initX)
	loc := &Location{
		X: make([]float64, dim),
	}
	copy(loc.X, initX)
	if method.Needs().Gradient {
		loc.Gradient = make([]float64, dim)
	}
	if method.Needs().Hessian {
		loc.Hessian = mat64.NewSymDense(dim, nil)
	}

	evalType := NoEvaluation
	if settings.UseInitialData {
		loc.F = settings.InitialFunctionValue
		if loc.Gradient != nil {
			initG := settings.InitialGradient
			if len(initG) != dim {
				panic("local: initial location size mismatch")
			}
			copy(loc.Gradient, initG)
		}
	} else {
		evalType = FuncEvaluation
		if loc.Gradient != nil {
			evalType = FuncGradEvaluation
		}
		evaluate(funcInfo, evalType, loc.X, loc, stats)
	}

	if math.IsNaN(loc.F) {
		return loc, evalType, ErrNaN
	}
	if math.IsInf(loc.F, 1) {
		return loc, evalType, ErrInf
	}
	for _, v := range loc.Gradient {
		if math.IsInf(v, 0) {
			return loc, evalType, ErrGradInf
		}
		if math.IsNaN(v) {
			return loc, evalType, ErrGradNaN
		}
	}

	return loc, evalType, nil
}

func checkConvergence(loc *Location, iterType IterationType, stats *Stats, settings *Settings) Status {
	if iterType == MajorIteration || iterType == InitIteration {
		if loc.Gradient != nil {
			norm := floats.Norm(loc.Gradient, math.Inf(1))
			if norm < settings.GradientAbsTol {
				return GradientAbsoluteConvergence
			}
		}
		if loc.F < settings.FunctionAbsTol {
			return FunctionAbsoluteConvergence
		}
	}

	// Check every step for negative infinity because it could break the
	// linesearches and -inf is the best you can do anyway.
	if math.IsInf(loc.F, -1) {
		return FunctionNegativeInfinity
	}

	if settings.FuncEvaluations > 0 {
		totalFun := stats.FuncEvaluations + stats.FuncGradEvaluations + stats.FuncGradHessEvaluations
		if totalFun >= settings.FuncEvaluations {
			return FunctionEvaluationLimit
		}
	}

	if settings.GradEvaluations > 0 {
		totalGrad := stats.GradEvaluations + stats.FuncGradEvaluations + stats.FuncGradHessEvaluations
		if totalGrad >= settings.GradEvaluations {
			return GradientEvaluationLimit
		}
	}

	if settings.HessEvaluations > 0 {
		totalHess := stats.HessEvaluations + stats.FuncGradHessEvaluations
		if totalHess >= settings.HessEvaluations {
			return HessianEvaluationLimit
		}
	}

	if settings.Runtime > 0 {
		// TODO(vladimir-ch): It would be nice to update Runtime here.
		if stats.Runtime >= settings.Runtime {
			return RuntimeLimit
		}
	}

	if iterType == MajorIteration && settings.MajorIterations > 0 {
		if stats.MajorIterations >= settings.MajorIterations {
			return IterationLimit
		}
	}
	return NotTerminated
}

func invalidate(loc *Location, x, f, grad, hess bool) {
	if x {
		for i := range loc.X {
			loc.X[i] = math.NaN()
		}
	}
	if f {
		loc.F = math.NaN()
	}
	if grad {
		for i := range loc.Gradient {
			loc.Gradient[i] = math.NaN()
		}
	}
	if hess && loc.Hessian != nil {
		n := loc.Hessian.Symmetric()
		for i := 0; i < n; i++ {
			for j := i; j < n; j++ {
				loc.Hessian.SetSym(i, j, math.NaN())
			}
		}
	}
}

// evaluate evaluates the function given by funcInfo at xNext, stores the
// answer into loc and updates stats. If loc.X is not equal to xNext, then
// unused fields of loc are set to NaN.
// evaluate panics if the function does not support the requested evalType.
func evaluate(funcInfo *functionInfo, evalType EvaluationType, xNext []float64, loc *Location, stats *Stats) {
	different := !floats.Equal(loc.X, xNext)
	if different {
		copy(loc.X, xNext)
	}
	switch evalType {
	case FuncEvaluation:
		if different {
			invalidate(loc, false, false, true, true)
		}
		loc.F = funcInfo.function.Func(loc.X)
		stats.FuncEvaluations++
		return
	case GradEvaluation:
		if funcInfo.IsGradient {
			if different {
				invalidate(loc, false, true, false, true)
			}
			funcInfo.gradient.Grad(loc.X, loc.Gradient)
			stats.GradEvaluations++
			return
		}
		if funcInfo.IsFunctionGradient {
			if different {
				invalidate(loc, false, false, false, true)
			}
			loc.F = funcInfo.functionGradient.FuncGrad(loc.X, loc.Gradient)
			stats.FuncGradEvaluations++
			return
		}
		if funcInfo.IsFunctionGradientHessian {
			if loc.Hessian == nil {
				loc.Hessian = mat64.NewSymDense(len(loc.X), nil)
			}
			loc.F = funcInfo.functionGradientHessian.FuncGradHess(loc.X, loc.Gradient, loc.Hessian)
			stats.FuncGradHessEvaluations++
			return
		}
	case HessEvaluation:
		if funcInfo.IsHessian {
			if different {
				invalidate(loc, false, true, true, false)
			}
			funcInfo.hessian.Hess(loc.X, loc.Hessian)
			stats.HessEvaluations++
			return
		}
		if funcInfo.IsFunctionGradientHessian {
			if loc.Gradient == nil {
				loc.Gradient = make([]float64, len(loc.X))
			}
			loc.F = funcInfo.functionGradientHessian.FuncGradHess(loc.X, loc.Gradient, loc.Hessian)
			stats.FuncGradHessEvaluations++
			return
		}
	case FuncGradEvaluation:
		if funcInfo.IsFunctionGradient {
			if different {
				invalidate(loc, false, false, false, true)
			}
			loc.F = funcInfo.functionGradient.FuncGrad(loc.X, loc.Gradient)
			stats.FuncGradEvaluations++
			return
		}
		if funcInfo.IsGradient {
			if different {
				invalidate(loc, false, false, false, true)
			}
			loc.F = funcInfo.function.Func(loc.X)
			stats.FuncEvaluations++
			funcInfo.gradient.Grad(loc.X, loc.Gradient)
			stats.GradEvaluations++
			return
		}
		if funcInfo.IsFunctionGradientHessian {
			if loc.Hessian == nil {
				loc.Hessian = mat64.NewSymDense(len(loc.X), nil)
			}
			loc.F = funcInfo.functionGradientHessian.FuncGradHess(loc.X, loc.Gradient, loc.Hessian)
			stats.FuncGradHessEvaluations++
			return
		}
	case FuncGradHessEvaluation:
		if funcInfo.IsFunctionGradientHessian {
			loc.F = funcInfo.functionGradientHessian.FuncGradHess(loc.X, loc.Gradient, loc.Hessian)
			stats.FuncGradHessEvaluations++
			return
		}
		if funcInfo.IsGradient && funcInfo.IsHessian {
			loc.F = funcInfo.function.Func(loc.X)
			stats.FuncEvaluations++
			funcInfo.gradient.Grad(loc.X, loc.Gradient)
			stats.GradEvaluations++
			funcInfo.hessian.Hess(loc.X, loc.Hessian)
			stats.HessEvaluations++
			return
		}
	case NoEvaluation:
		if different {
			// The evaluation point xNext is not equal to loc.X, so we fill loc
			// with NaNs to indicate that it does not contain a valid
			// evaluation result.
			invalidate(loc, true, true, true, true)
		}
		return
	default:
		panic(fmt.Sprintf("unknown evaluation type %v", evalType))
	}
	panic(fmt.Sprintf("objective function does not support %v", evalType))
}

// update updates the stats given the new evaluation
func update(loc *Location, optLoc *Location, stats *Stats, iterType IterationType, startTime time.Time) {
	if iterType == MajorIteration {
		stats.MajorIterations++
	}
	if loc.F <= optLoc.F {
		copyLocation(optLoc, loc)
	}
	stats.Runtime = time.Since(startTime)
}
<commit_msg>Panic in evaluate() when NoEvaluation && loc.X != nextX<commit_after>// Copyright ©2014 The gonum 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 optimize

import (
	"fmt"
	"math"
	"time"

	"github.com/gonum/floats"
	"github.com/gonum/matrix/mat64"
)

// Local finds a local minimum of a function using a sequential algorithm.
// In order to maximize a function, multiply the output by -1.
//
// The first argument is of Function type representing the function to be minimized.
// Type switching is used to see if the function implements Gradient, FunctionGradient
// and Statuser.
//
// The second argument is the initial location at which to start the minimization.
// The initial location must be supplied, and must have a length equal to the
// problem dimension.
//
// The third argument contains the settings for the minimization. It is here that
// gradient tolerance, etc. are specified. The DefaultSettings() function
// can be called for a Settings struct with the default values initialized.
// If settings == nil, the default settings are used. Please see the documentation
// for the Settings structure for more information. The optimization Method used
// may also contain settings, see documentation for the appropriate optimizer.
//
// The final argument is the optimization method to use. If method == nil, then
// an appropriate default is chosen based on the properties of the other arguments
// (dimension, gradient-free or gradient-based, etc.). The optimization
// methods in this package are designed such that reasonable defaults occur
// if options are not specified explicitly. For example, the code
//  method := &Bfgs{}
// creates a pointer to a new Bfgs struct. When minimize is called, the settings
// in the method will be populated with default values. The methods are also
// designed such that they can be reused in future calls to method.
//
// Local returns a Result struct and any error that occurred. Please see the
// documentation of Result for more information.
//
// Please be aware that the default behavior of Local is to find the minimum.
// For certain functions and optimization methods, this process can take many
// function evaluations. If you would like to put limits on this, for example
// maximum runtime or maximum function evaluations, please modify the Settings
// input struct.
func Local(f Function, initX []float64, settings *Settings, method Method) (*Result, error) {
	if len(initX) == 0 {
		panic("local: initial X has zero length")
	}

	startTime := time.Now()
	funcInfo := newFunctionInfo(f)
	if method == nil {
		method = getDefaultMethod(funcInfo)
	}
	if err := funcInfo.satisfies(method); err != nil {
		return nil, err
	}

	if funcInfo.IsStatuser {
		_, err := funcInfo.statuser.Status()
		if err != nil {
			return nil, err
		}
	}

	if settings == nil {
		settings = DefaultSettings()
	}

	if settings.Recorder != nil {
		// Initialize Recorder first. If it fails, we avoid the (possibly
		// time-consuming) evaluation of F and DF at the starting location.
		err := settings.Recorder.Init(&funcInfo.FunctionInfo)
		if err != nil {
			return nil, err
		}
	}

	stats := &Stats{}
	optLoc, evalType, err := getStartingLocation(funcInfo, method, initX, stats, settings)
	if err != nil {
		return nil, err
	}

	// Runtime is the only Stats field that needs to be updated here.
	stats.Runtime = time.Since(startTime)
	// Send optLoc to Recorder before checking it for convergence.
	if settings.Recorder != nil {
		err = settings.Recorder.Record(optLoc, evalType, InitIteration, stats)
	}

	// Check if the starting location satisfies the convergence criteria.
	status := checkConvergence(optLoc, InitIteration, stats, settings)
	if status == NotTerminated && err == nil {
		// The starting location is not good enough, we need to perform a
		// minimization. The optimal location will be stored in-place in
		// optLoc.
		status, err = minimize(settings, method, funcInfo, stats, optLoc, startTime)
	}

	if settings.Recorder != nil && err == nil {
		// Send the optimal location to Recorder.
		err = settings.Recorder.Record(optLoc, NoEvaluation, PostIteration, stats)
	}
	stats.Runtime = time.Since(startTime)
	return &Result{
		Location: *optLoc,
		Stats:    *stats,
		Status:   status,
	}, err
}

func minimize(settings *Settings, method Method, funcInfo *functionInfo, stats *Stats, optLoc *Location, startTime time.Time) (status Status, err error) {
	loc := &Location{}
	copyLocation(loc, optLoc)
	xNext := make([]float64, len(loc.X))

	methodStatus, methodIsStatuser := method.(Statuser)

	evalType, iterType, err := method.Init(loc, &funcInfo.FunctionInfo, xNext)
	if err != nil {
		return Failure, err
	}

	for {
		if funcInfo.IsStatuser {
			// Check the function status before evaluating.
			status, err = funcInfo.statuser.Status()
			if err != nil || status != NotTerminated {
				return
			}
		}

		// Perform evalType evaluation of the function at xNext and store the
		// result in location.
		evaluate(funcInfo, evalType, xNext, loc, stats)
		// Update the stats and optLoc.
		update(loc, optLoc, stats, iterType, startTime)
		// Get the convergence status before recording the new location.
		status = checkConvergence(optLoc, iterType, stats, settings)

		if settings.Recorder != nil {
			err = settings.Recorder.Record(loc, evalType, iterType, stats)
			if err != nil {
				if status == NotTerminated {
					status = Failure
				}
				return
			}
		}

		if status != NotTerminated {
			return
		}

		if methodIsStatuser {
			status, err = methodStatus.Status()
			if err != nil || status != NotTerminated {
				return
			}
		}

		// Find the next location (stored in-place into xNext).
		evalType, iterType, err = method.Iterate(loc, xNext)
		if err != nil {
			status = Failure
			return
		}
	}
	panic("unreachable")
}

func copyLocation(dst, src *Location) {
	dst.X = resize(dst.X, len(src.X))
	copy(dst.X, src.X)

	dst.F = src.F

	dst.Gradient = resize(dst.Gradient, len(src.Gradient))
	copy(dst.Gradient, src.Gradient)
}

func getDefaultMethod(funcInfo *functionInfo) Method {
	if funcInfo.IsGradient || funcInfo.IsFunctionGradient {
		return &BFGS{}
	}
	// TODO: Implement a gradient-free method
	panic("optimize: gradient-free methods not yet coded")
}

// getStartingLocation allocates and initializes the starting location for the minimization.
func getStartingLocation(funcInfo *functionInfo, method Method, initX []float64, stats *Stats, settings *Settings) (*Location, EvaluationType, error) {
	dim := len(initX)
	loc := &Location{
		X: make([]float64, dim),
	}
	copy(loc.X, initX)
	if method.Needs().Gradient {
		loc.Gradient = make([]float64, dim)
	}
	if method.Needs().Hessian {
		loc.Hessian = mat64.NewSymDense(dim, nil)
	}

	evalType := NoEvaluation
	if settings.UseInitialData {
		loc.F = settings.InitialFunctionValue
		if loc.Gradient != nil {
			initG := settings.InitialGradient
			if len(initG) != dim {
				panic("local: initial location size mismatch")
			}
			copy(loc.Gradient, initG)
		}
	} else {
		evalType = FuncEvaluation
		if loc.Gradient != nil {
			evalType = FuncGradEvaluation
		}
		evaluate(funcInfo, evalType, loc.X, loc, stats)
	}

	if math.IsNaN(loc.F) {
		return loc, evalType, ErrNaN
	}
	if math.IsInf(loc.F, 1) {
		return loc, evalType, ErrInf
	}
	for _, v := range loc.Gradient {
		if math.IsInf(v, 0) {
			return loc, evalType, ErrGradInf
		}
		if math.IsNaN(v) {
			return loc, evalType, ErrGradNaN
		}
	}

	return loc, evalType, nil
}

func checkConvergence(loc *Location, iterType IterationType, stats *Stats, settings *Settings) Status {
	if iterType == MajorIteration || iterType == InitIteration {
		if loc.Gradient != nil {
			norm := floats.Norm(loc.Gradient, math.Inf(1))
			if norm < settings.GradientAbsTol {
				return GradientAbsoluteConvergence
			}
		}
		if loc.F < settings.FunctionAbsTol {
			return FunctionAbsoluteConvergence
		}
	}

	// Check every step for negative infinity because it could break the
	// linesearches and -inf is the best you can do anyway.
	if math.IsInf(loc.F, -1) {
		return FunctionNegativeInfinity
	}

	if settings.FuncEvaluations > 0 {
		totalFun := stats.FuncEvaluations + stats.FuncGradEvaluations + stats.FuncGradHessEvaluations
		if totalFun >= settings.FuncEvaluations {
			return FunctionEvaluationLimit
		}
	}

	if settings.GradEvaluations > 0 {
		totalGrad := stats.GradEvaluations + stats.FuncGradEvaluations + stats.FuncGradHessEvaluations
		if totalGrad >= settings.GradEvaluations {
			return GradientEvaluationLimit
		}
	}

	if settings.HessEvaluations > 0 {
		totalHess := stats.HessEvaluations + stats.FuncGradHessEvaluations
		if totalHess >= settings.HessEvaluations {
			return HessianEvaluationLimit
		}
	}

	if settings.Runtime > 0 {
		// TODO(vladimir-ch): It would be nice to update Runtime here.
		if stats.Runtime >= settings.Runtime {
			return RuntimeLimit
		}
	}

	if iterType == MajorIteration && settings.MajorIterations > 0 {
		if stats.MajorIterations >= settings.MajorIterations {
			return IterationLimit
		}
	}
	return NotTerminated
}

func invalidate(loc *Location, f, grad, hess bool) {
	if f {
		loc.F = math.NaN()
	}
	if grad {
		for i := range loc.Gradient {
			loc.Gradient[i] = math.NaN()
		}
	}
	if hess && loc.Hessian != nil {
		n := loc.Hessian.Symmetric()
		for i := 0; i < n; i++ {
			for j := i; j < n; j++ {
				loc.Hessian.SetSym(i, j, math.NaN())
			}
		}
	}
}

// evaluate evaluates the function given by funcInfo at xNext, stores the
// answer into loc and updates stats. If loc.X is not equal to xNext, then
// unused fields of loc are set to NaN.
// evaluate panics if the function does not support the requested evalType.
func evaluate(funcInfo *functionInfo, evalType EvaluationType, xNext []float64, loc *Location, stats *Stats) {
	different := !floats.Equal(loc.X, xNext)
	if different {
		copy(loc.X, xNext)
	}
	switch evalType {
	case FuncEvaluation:
		if different {
			invalidate(loc, false, true, true)
		}
		loc.F = funcInfo.function.Func(loc.X)
		stats.FuncEvaluations++
		return
	case GradEvaluation:
		if funcInfo.IsGradient {
			if different {
				invalidate(loc, true, false, true)
			}
			funcInfo.gradient.Grad(loc.X, loc.Gradient)
			stats.GradEvaluations++
			return
		}
		if funcInfo.IsFunctionGradient {
			if different {
				invalidate(loc, false, false, true)
			}
			loc.F = funcInfo.functionGradient.FuncGrad(loc.X, loc.Gradient)
			stats.FuncGradEvaluations++
			return
		}
		if funcInfo.IsFunctionGradientHessian {
			if loc.Hessian == nil {
				loc.Hessian = mat64.NewSymDense(len(loc.X), nil)
			}
			loc.F = funcInfo.functionGradientHessian.FuncGradHess(loc.X, loc.Gradient, loc.Hessian)
			stats.FuncGradHessEvaluations++
			return
		}
	case HessEvaluation:
		if funcInfo.IsHessian {
			if different {
				invalidate(loc, true, true, false)
			}
			funcInfo.hessian.Hess(loc.X, loc.Hessian)
			stats.HessEvaluations++
			return
		}
		if funcInfo.IsFunctionGradientHessian {
			if loc.Gradient == nil {
				loc.Gradient = make([]float64, len(loc.X))
			}
			loc.F = funcInfo.functionGradientHessian.FuncGradHess(loc.X, loc.Gradient, loc.Hessian)
			stats.FuncGradHessEvaluations++
			return
		}
	case FuncGradEvaluation:
		if funcInfo.IsFunctionGradient {
			if different {
				invalidate(loc, false, false, true)
			}
			loc.F = funcInfo.functionGradient.FuncGrad(loc.X, loc.Gradient)
			stats.FuncGradEvaluations++
			return
		}
		if funcInfo.IsGradient {
			if different {
				invalidate(loc, false, false, true)
			}
			loc.F = funcInfo.function.Func(loc.X)
			stats.FuncEvaluations++
			funcInfo.gradient.Grad(loc.X, loc.Gradient)
			stats.GradEvaluations++
			return
		}
		if funcInfo.IsFunctionGradientHessian {
			if loc.Hessian == nil {
				loc.Hessian = mat64.NewSymDense(len(loc.X), nil)
			}
			loc.F = funcInfo.functionGradientHessian.FuncGradHess(loc.X, loc.Gradient, loc.Hessian)
			stats.FuncGradHessEvaluations++
			return
		}
	case FuncGradHessEvaluation:
		if funcInfo.IsFunctionGradientHessian {
			loc.F = funcInfo.functionGradientHessian.FuncGradHess(loc.X, loc.Gradient, loc.Hessian)
			stats.FuncGradHessEvaluations++
			return
		}
		if funcInfo.IsGradient && funcInfo.IsHessian {
			loc.F = funcInfo.function.Func(loc.X)
			stats.FuncEvaluations++
			funcInfo.gradient.Grad(loc.X, loc.Gradient)
			stats.GradEvaluations++
			funcInfo.hessian.Hess(loc.X, loc.Hessian)
			stats.HessEvaluations++
			return
		}
	case NoEvaluation:
		if different {
			// Optimizers should not request NoEvaluation at a new location.
			// The intent and therefore an appropriate action are both unclear.
			panic("no evaluation requested at new location")
		}
		return
	default:
		panic(fmt.Sprintf("unknown evaluation type %v", evalType))
	}
	panic(fmt.Sprintf("objective function does not support %v", evalType))
}

// update updates the stats given the new evaluation
func update(loc *Location, optLoc *Location, stats *Stats, iterType IterationType, startTime time.Time) {
	if iterType == MajorIteration {
		stats.MajorIterations++
	}
	if loc.F <= optLoc.F {
		copyLocation(optLoc, loc)
	}
	stats.Runtime = time.Since(startTime)
}
<|endoftext|>


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

package source

import (
	"context"
	"fmt"
	"go/ast"
	"go/parser"
	"go/scanner"
	"go/token"
	"go/types"
	"path/filepath"
	"strings"

	"golang.org/x/tools/internal/lsp/fuzzy"
	"golang.org/x/tools/internal/lsp/protocol"
	"golang.org/x/tools/internal/span"
	errors "golang.org/x/xerrors"
)

// packageClauseCompletions offers completions for a package declaration when
// one is not present in the given file.
func packageClauseCompletions(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position) ([]CompletionItem, *Selection, error) {
	// We know that the AST for this file will be empty due to the missing
	// package declaration, but parse it anyway to get a mapper.
	pgf, err := snapshot.ParseGo(ctx, fh, ParseFull)
	if err != nil {
		return nil, nil, err
	}

	cursorSpan, err := pgf.Mapper.PointSpan(pos)
	if err != nil {
		return nil, nil, err
	}
	rng, err := cursorSpan.Range(pgf.Mapper.Converter)
	if err != nil {
		return nil, nil, err
	}

	surrounding, err := packageCompletionSurrounding(ctx, snapshot.FileSet(), fh, pgf, rng.Start)
	if err != nil {
		return nil, nil, fmt.Errorf("invalid position for package completion: %w", err)
	}

	packageSuggestions, err := packageSuggestions(ctx, snapshot, fh.URI(), "")
	if err != nil {
		return nil, nil, err
	}

	var items []CompletionItem
	for _, pkg := range packageSuggestions {
		insertText := fmt.Sprintf("package %s", pkg.name)
		items = append(items, CompletionItem{
			Label:      insertText,
			Kind:       protocol.ModuleCompletion,
			InsertText: insertText,
			Score:      pkg.score,
		})
	}

	return items, surrounding, nil
}

// packageCompletionSurrounding returns surrounding for package completion if a
// package completions can be suggested at a given position. A valid location
// for package completion is above any declarations or import statements.
func packageCompletionSurrounding(ctx context.Context, fset *token.FileSet, fh FileHandle, pgf *ParsedGoFile, pos token.Pos) (*Selection, error) {
	src, err := fh.Read()
	if err != nil {
		return nil, err
	}

	// If the file lacks a package declaration, the parser will return an empty
	// AST. As a work-around, try to parse an expression from the file contents.
	expr, _ := parser.ParseExprFrom(fset, fh.URI().Filename(), src, parser.Mode(0))
	if expr == nil {
		return nil, fmt.Errorf("unparseable file (%s)", fh.URI())
	}
	tok := fset.File(expr.Pos())
	cursor := tok.Pos(pgf.Tok.Offset(pos))
	m := &protocol.ColumnMapper{
		URI:       pgf.URI,
		Content:   src,
		Converter: span.NewContentConverter(fh.URI().Filename(), src),
	}

	// If we were able to parse out an identifier as the first expression from
	// the file, it may be the beginning of a package declaration ("pack ").
	// We can offer package completions if the cursor is in the identifier.
	if name, ok := expr.(*ast.Ident); ok {
		if cursor >= name.Pos() && cursor <= name.End() {
			if !strings.HasPrefix(PACKAGE, name.Name) {
				return nil, fmt.Errorf("cursor in non-matching ident")
			}
			return &Selection{
				content:     name.Name,
				cursor:      cursor,
				mappedRange: newMappedRange(fset, m, name.Pos(), name.End()),
			}, nil
		}
	}

	// The file is invalid, but it contains an expression that we were able to
	// parse. We will use this expression to construct the cursor's
	// "surrounding".

	// First, consider the possibility that we have a valid "package" keyword
	// with an empty package name ("package "). "package" is parsed as an
	// *ast.BadDecl since it is a keyword. This logic would allow "package" to
	// appear on any line of the file as long as it's the first code expression
	// in the file.
	lines := strings.Split(string(src), "\n")
	cursorLine := tok.Line(cursor)
	if cursorLine <= 0 || cursorLine > len(lines) {
		return nil, fmt.Errorf("invalid line number")
	}
	if fset.Position(expr.Pos()).Line == cursorLine {
		words := strings.Fields(lines[cursorLine-1])
		if len(words) > 0 && words[0] == PACKAGE {
			content := PACKAGE
			// Account for spaces if there are any.
			if len(words) > 1 {
				content += " "
			}

			start := expr.Pos()
			end := token.Pos(int(expr.Pos()) + len(content) + 1)
			// We have verified that we have a valid 'package' keyword as our
			// first expression. Ensure that cursor is in this keyword or
			// otherwise fallback to the general case.
			if cursor >= start && cursor <= end {
				return &Selection{
					content:     content,
					cursor:      cursor,
					mappedRange: newMappedRange(fset, m, start, end),
				}, nil
			}
		}
	}

	// If the cursor is after the start of the expression, no package
	// declaration will be valid.
	if cursor > expr.Pos() {
		return nil, fmt.Errorf("cursor after expression")
	}

	// If the cursor is in a comment, don't offer any completions.
	if cursorInComment(fset, cursor, src) {
		return nil, fmt.Errorf("cursor in comment")
	}

	// The surrounding range in this case is the cursor except for empty file,
	// in which case it's end of file - 1
	start, end := cursor, cursor
	if tok.Size() == 0 {
		start, end = tok.Pos(0)-1, tok.Pos(0)-1
	}

	return &Selection{
		content:     "",
		cursor:      cursor,
		mappedRange: newMappedRange(fset, m, start, end),
	}, nil
}

func cursorInComment(fset *token.FileSet, cursor token.Pos, src []byte) bool {
	var s scanner.Scanner
	s.Init(fset.File(cursor), src, func(_ token.Position, _ string) {}, scanner.ScanComments)
	for {
		pos, tok, lit := s.Scan()
		if pos <= cursor && cursor <= token.Pos(int(pos)+len(lit)) {
			return tok == token.COMMENT
		}
		if tok == token.EOF {
			break
		}
	}
	return false
}

// packageNameCompletions returns name completions for a package clause using
// the current name as prefix.
func (c *completer) packageNameCompletions(ctx context.Context, fileURI span.URI, name *ast.Ident) error {
	cursor := int(c.pos - name.NamePos)
	if cursor < 0 || cursor > len(name.Name) {
		return errors.New("cursor is not in package name identifier")
	}

	prefix := name.Name[:cursor]
	packageSuggestions, err := packageSuggestions(ctx, c.snapshot, fileURI, prefix)
	if err != nil {
		return err
	}

	for _, pkg := range packageSuggestions {
		if item, err := c.item(ctx, pkg); err == nil {
			c.items = append(c.items, item)
		}
	}
	return nil
}

// packageSuggestions returns a list of packages from workspace packages that
// have the given prefix and are used in the the same directory as the given
// file. This also includes test packages for these packages (<pkg>_test) and
// the directory name itself.
func packageSuggestions(ctx context.Context, snapshot Snapshot, fileURI span.URI, prefix string) ([]candidate, error) {
	workspacePackages, err := snapshot.WorkspacePackages(ctx)
	if err != nil {
		return nil, err
	}

	dirPath := filepath.Dir(string(fileURI))
	dirName := filepath.Base(dirPath)

	seenPkgs := make(map[string]struct{})

	toCandidate := func(name string, score float64) candidate {
		obj := types.NewPkgName(0, nil, name, types.NewPackage("", name))
		return candidate{obj: obj, name: name, score: score}
	}

	matcher := fuzzy.NewMatcher(prefix)

	// The `go` command by default only allows one package per directory but we
	// support multiple package suggestions since gopls is build system agnostic.
	var packages []candidate
	for _, pkg := range workspacePackages {
		if pkg.Name() == "main" {
			continue
		}
		if _, ok := seenPkgs[pkg.Name()]; ok {
			continue
		}

		// Only add packages that are previously used in the current directory.
		var relevantPkg bool
		for _, pgf := range pkg.CompiledGoFiles() {
			if filepath.Dir(string(pgf.URI)) == dirPath {
				relevantPkg = true
				break
			}
		}
		if !relevantPkg {
			continue
		}

		// Add a found package used in current directory as a high relevance
		// suggestion and the test package for it as a medium relevance
		// suggestion.
		if score := float64(matcher.Score(pkg.Name())); score > 0 {
			packages = append(packages, toCandidate(pkg.Name(), score*highScore))
		}
		seenPkgs[pkg.Name()] = struct{}{}

		testPkgName := pkg.Name() + "_test"
		if _, ok := seenPkgs[testPkgName]; ok || strings.HasSuffix(pkg.Name(), "_test") {
			continue
		}
		if score := float64(matcher.Score(testPkgName)); score > 0 {
			packages = append(packages, toCandidate(testPkgName, score*stdScore))
		}
		seenPkgs[testPkgName] = struct{}{}
	}

	// Add current directory name as a low relevance suggestion.
	if _, ok := seenPkgs[dirName]; !ok {
		if score := float64(matcher.Score(dirName)); score > 0 {
			packages = append(packages, toCandidate(dirName, score*lowScore))
		}

		testDirName := dirName + "_test"
		if score := float64(matcher.Score(testDirName)); score > 0 {
			packages = append(packages, toCandidate(testDirName, score*lowScore))
		}
	}

	if score := float64(matcher.Score("main")); score > 0 {
		packages = append(packages, toCandidate("main", score*lowScore))
	}

	return packages, nil
}
<commit_msg>internal/lsp/source: fix go1.12 build (again)<commit_after>// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package source

import (
	"context"
	"fmt"
	"go/ast"
	"go/parser"
	"go/scanner"
	"go/token"
	"go/types"
	"path/filepath"
	"strings"

	"golang.org/x/tools/internal/lsp/fuzzy"
	"golang.org/x/tools/internal/lsp/protocol"
	"golang.org/x/tools/internal/span"
	errors "golang.org/x/xerrors"
)

// packageClauseCompletions offers completions for a package declaration when
// one is not present in the given file.
func packageClauseCompletions(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position) ([]CompletionItem, *Selection, error) {
	// We know that the AST for this file will be empty due to the missing
	// package declaration, but parse it anyway to get a mapper.
	pgf, err := snapshot.ParseGo(ctx, fh, ParseFull)
	if err != nil {
		return nil, nil, err
	}

	cursorSpan, err := pgf.Mapper.PointSpan(pos)
	if err != nil {
		return nil, nil, err
	}
	rng, err := cursorSpan.Range(pgf.Mapper.Converter)
	if err != nil {
		return nil, nil, err
	}

	surrounding, err := packageCompletionSurrounding(ctx, snapshot.FileSet(), fh, pgf, rng.Start)
	if err != nil {
		return nil, nil, errors.Errorf("invalid position for package completion: %w", err)
	}

	packageSuggestions, err := packageSuggestions(ctx, snapshot, fh.URI(), "")
	if err != nil {
		return nil, nil, err
	}

	var items []CompletionItem
	for _, pkg := range packageSuggestions {
		insertText := fmt.Sprintf("package %s", pkg.name)
		items = append(items, CompletionItem{
			Label:      insertText,
			Kind:       protocol.ModuleCompletion,
			InsertText: insertText,
			Score:      pkg.score,
		})
	}

	return items, surrounding, nil
}

// packageCompletionSurrounding returns surrounding for package completion if a
// package completions can be suggested at a given position. A valid location
// for package completion is above any declarations or import statements.
func packageCompletionSurrounding(ctx context.Context, fset *token.FileSet, fh FileHandle, pgf *ParsedGoFile, pos token.Pos) (*Selection, error) {
	src, err := fh.Read()
	if err != nil {
		return nil, err
	}

	// If the file lacks a package declaration, the parser will return an empty
	// AST. As a work-around, try to parse an expression from the file contents.
	expr, _ := parser.ParseExprFrom(fset, fh.URI().Filename(), src, parser.Mode(0))
	if expr == nil {
		return nil, fmt.Errorf("unparseable file (%s)", fh.URI())
	}
	tok := fset.File(expr.Pos())
	cursor := tok.Pos(pgf.Tok.Offset(pos))
	m := &protocol.ColumnMapper{
		URI:       pgf.URI,
		Content:   src,
		Converter: span.NewContentConverter(fh.URI().Filename(), src),
	}

	// If we were able to parse out an identifier as the first expression from
	// the file, it may be the beginning of a package declaration ("pack ").
	// We can offer package completions if the cursor is in the identifier.
	if name, ok := expr.(*ast.Ident); ok {
		if cursor >= name.Pos() && cursor <= name.End() {
			if !strings.HasPrefix(PACKAGE, name.Name) {
				return nil, fmt.Errorf("cursor in non-matching ident")
			}
			return &Selection{
				content:     name.Name,
				cursor:      cursor,
				mappedRange: newMappedRange(fset, m, name.Pos(), name.End()),
			}, nil
		}
	}

	// The file is invalid, but it contains an expression that we were able to
	// parse. We will use this expression to construct the cursor's
	// "surrounding".

	// First, consider the possibility that we have a valid "package" keyword
	// with an empty package name ("package "). "package" is parsed as an
	// *ast.BadDecl since it is a keyword. This logic would allow "package" to
	// appear on any line of the file as long as it's the first code expression
	// in the file.
	lines := strings.Split(string(src), "\n")
	cursorLine := tok.Line(cursor)
	if cursorLine <= 0 || cursorLine > len(lines) {
		return nil, fmt.Errorf("invalid line number")
	}
	if fset.Position(expr.Pos()).Line == cursorLine {
		words := strings.Fields(lines[cursorLine-1])
		if len(words) > 0 && words[0] == PACKAGE {
			content := PACKAGE
			// Account for spaces if there are any.
			if len(words) > 1 {
				content += " "
			}

			start := expr.Pos()
			end := token.Pos(int(expr.Pos()) + len(content) + 1)
			// We have verified that we have a valid 'package' keyword as our
			// first expression. Ensure that cursor is in this keyword or
			// otherwise fallback to the general case.
			if cursor >= start && cursor <= end {
				return &Selection{
					content:     content,
					cursor:      cursor,
					mappedRange: newMappedRange(fset, m, start, end),
				}, nil
			}
		}
	}

	// If the cursor is after the start of the expression, no package
	// declaration will be valid.
	if cursor > expr.Pos() {
		return nil, fmt.Errorf("cursor after expression")
	}

	// If the cursor is in a comment, don't offer any completions.
	if cursorInComment(fset, cursor, src) {
		return nil, fmt.Errorf("cursor in comment")
	}

	// The surrounding range in this case is the cursor except for empty file,
	// in which case it's end of file - 1
	start, end := cursor, cursor
	if tok.Size() == 0 {
		start, end = tok.Pos(0)-1, tok.Pos(0)-1
	}

	return &Selection{
		content:     "",
		cursor:      cursor,
		mappedRange: newMappedRange(fset, m, start, end),
	}, nil
}

func cursorInComment(fset *token.FileSet, cursor token.Pos, src []byte) bool {
	var s scanner.Scanner
	s.Init(fset.File(cursor), src, func(_ token.Position, _ string) {}, scanner.ScanComments)
	for {
		pos, tok, lit := s.Scan()
		if pos <= cursor && cursor <= token.Pos(int(pos)+len(lit)) {
			return tok == token.COMMENT
		}
		if tok == token.EOF {
			break
		}
	}
	return false
}

// packageNameCompletions returns name completions for a package clause using
// the current name as prefix.
func (c *completer) packageNameCompletions(ctx context.Context, fileURI span.URI, name *ast.Ident) error {
	cursor := int(c.pos - name.NamePos)
	if cursor < 0 || cursor > len(name.Name) {
		return errors.New("cursor is not in package name identifier")
	}

	prefix := name.Name[:cursor]
	packageSuggestions, err := packageSuggestions(ctx, c.snapshot, fileURI, prefix)
	if err != nil {
		return err
	}

	for _, pkg := range packageSuggestions {
		if item, err := c.item(ctx, pkg); err == nil {
			c.items = append(c.items, item)
		}
	}
	return nil
}

// packageSuggestions returns a list of packages from workspace packages that
// have the given prefix and are used in the the same directory as the given
// file. This also includes test packages for these packages (<pkg>_test) and
// the directory name itself.
func packageSuggestions(ctx context.Context, snapshot Snapshot, fileURI span.URI, prefix string) ([]candidate, error) {
	workspacePackages, err := snapshot.WorkspacePackages(ctx)
	if err != nil {
		return nil, err
	}

	dirPath := filepath.Dir(string(fileURI))
	dirName := filepath.Base(dirPath)

	seenPkgs := make(map[string]struct{})

	toCandidate := func(name string, score float64) candidate {
		obj := types.NewPkgName(0, nil, name, types.NewPackage("", name))
		return candidate{obj: obj, name: name, score: score}
	}

	matcher := fuzzy.NewMatcher(prefix)

	// The `go` command by default only allows one package per directory but we
	// support multiple package suggestions since gopls is build system agnostic.
	var packages []candidate
	for _, pkg := range workspacePackages {
		if pkg.Name() == "main" {
			continue
		}
		if _, ok := seenPkgs[pkg.Name()]; ok {
			continue
		}

		// Only add packages that are previously used in the current directory.
		var relevantPkg bool
		for _, pgf := range pkg.CompiledGoFiles() {
			if filepath.Dir(string(pgf.URI)) == dirPath {
				relevantPkg = true
				break
			}
		}
		if !relevantPkg {
			continue
		}

		// Add a found package used in current directory as a high relevance
		// suggestion and the test package for it as a medium relevance
		// suggestion.
		if score := float64(matcher.Score(pkg.Name())); score > 0 {
			packages = append(packages, toCandidate(pkg.Name(), score*highScore))
		}
		seenPkgs[pkg.Name()] = struct{}{}

		testPkgName := pkg.Name() + "_test"
		if _, ok := seenPkgs[testPkgName]; ok || strings.HasSuffix(pkg.Name(), "_test") {
			continue
		}
		if score := float64(matcher.Score(testPkgName)); score > 0 {
			packages = append(packages, toCandidate(testPkgName, score*stdScore))
		}
		seenPkgs[testPkgName] = struct{}{}
	}

	// Add current directory name as a low relevance suggestion.
	if _, ok := seenPkgs[dirName]; !ok {
		if score := float64(matcher.Score(dirName)); score > 0 {
			packages = append(packages, toCandidate(dirName, score*lowScore))
		}

		testDirName := dirName + "_test"
		if score := float64(matcher.Score(testDirName)); score > 0 {
			packages = append(packages, toCandidate(testDirName, score*lowScore))
		}
	}

	if score := float64(matcher.Score("main")); score > 0 {
		packages = append(packages, toCandidate("main", score*lowScore))
	}

	return packages, nil
}
<|endoftext|>


<commit_before>package facebook

import (
	"http"
	"io/ioutil"
	"json"
	"os"
	"strconv"
)

const (
	GRAPHURL = "http://graph.facebook.com/"
)

type Metadata struct {
	Connections Connections
	// Name, Description
	Fields map[string]string
}

type Connections struct {
	Albums   string
	Statuses string
	Links    string
	Posts    string
	Notes    string
	Videos   string
	Feed     string
	Photos   string
	Tagged   string
	Events   string
}

type Page struct {
	ID              string
	Name            string
	Picture         string
	Link            string
	Category        string
	Website         string
	Username        string
	Founded         string
	CompanyOverview string
	Mission         string
	Products        string
	FanCount        float64
}

func (p *Page) String() string {
	return "ID: " + p.ID + "\tName: " + p.Name + "\tPicture: " + p.Picture +
		"\tLink: " + p.Link + "\tCategory: " + p.Category + "\tWebsite: " +
		p.Website + "\tUsername: " + p.Username + "\tFounded: " + p.Founded +
		"\tCompany overview: " + p.CompanyOverview + "\tMission: " + p.Mission +
		"\tProducts: " + p.Products + "\tFan count:" +
		strconv.Ftoa64(p.FanCount, 'e', -1) + "\n"
}

type Person struct {
	ID          string
	Name        string
	FirstName   string
	LastName    string
	Link        string
	Gender      string
	Locale      string
	UpdatedTime string
}

func (p *Person) String() string {
	return "ID: " + p.ID + "\tName: " + p.Name + "\tFirst name: " + p.FirstName +
		"\tLast name: " + p.LastName + "\tLink: " + p.Link + "\tGender: " +
		p.Gender + "\tLocale: " + p.Locale + "\tUpdated time: " + p.UpdatedTime +
		"\n"
}

func FetchPageIntrospect(id string) (page Page, err os.Error) {
	return FetchPage(id + "?metadata=1")
}

func FetchPage(id string) (page Page, err os.Error) {
	body, err := fetchBody(id)
	if err != nil {
		return
	}
	data, err := getJsonMap(body)
	if err != nil {
		return
	}
	for key, value := range data {
		switch key {
		case "website":
			page.Website = value.(string)
		case "picture":
			page.Picture = value.(string)
		case "fan_count":
			page.FanCount = value.(float64)
		case "mission":
			page.Mission = value.(string)
		case "category":
			page.Category = value.(string)
		case "name":
			page.Name = value.(string)
		case "username":
			page.Username = value.(string)
		case "link":
			page.Link = value.(string)
		case "id":
			page.ID = value.(string)
		case "products":
			page.Products = value.(string)
		case "founded":
			page.Founded = value.(string)
		case "company_overview":
			page.CompanyOverview = value.(string)
		case "metadata":
			parseMetaData(value)
		}
	}
	return
}

func FetchPersonIntrospect(name string) (person Person, err os.Error) {
	return FetchPerson(name + "?metadata=1")
}

func FetchPerson(name string) (person Person, err os.Error) {
	body, err := fetchBody(name)
	if err != nil {
		return
	}
	data, err := getJsonMap(body)
	if err != nil {
		return
	}
	for key, value := range data {
		switch key {
		case "locale":
			person.Locale = value.(string)
		case "name":
			person.Name = value.(string)
		case "link":
			person.Link = value.(string)
		case "Gender":
			person.Gender = value.(string)
		case "first_name":
			person.FirstName = value.(string)
		case "last_name":
			person.LastName = value.(string)
		case "id":
			person.ID = value.(string)
		case "metadata":
			parseMetaData(value)
		}
	}
	return
}

func parseMetaData(value interface{}) (metadata Metadata) {
	data := value.(map[string]interface{})
	for key, v := range data {
		switch key {
		case "connections":
			metadata.Connections = parseConnections(v)
		case "fields":
			metadata.Fields = parseFields(v)
		}
	}
	return
}

func parseConnections(value interface{}) (connections Connections) {
	data := value.(map[string]interface{})
	for key, v := range data {
		switch key {
		case "feed":
			connections.Feed = v.(string)
		case "posts":
			connections.Posts = v.(string)
		case "tagged":
			connections.Tagged = v.(string)
		case "statuses":
			connections.Statuses = v.(string)
		case "links":
			connections.Links = v.(string)
		case "notes":
			connections.Notes = v.(string)
		case "photos":
			connections.Photos = v.(string)
		case "albums":
			connections.Albums = v.(string)
		case "events":
			connections.Events = v.(string)
		case "videos":
			connections.Videos = v.(string)
		}
	}
	return
}

func parseFields(value interface{}) (fields map[string]string) {
	fields = make(map[string]string)
	var field map[string]interface{}
	data := value.([]interface{})
	for _, c := range data {
		field = c.(map[string]interface{})
		fields[field["name"].(string)] = field["description"].(string)
	}
	return
}

func getJsonMap(body []byte) (data map[string]interface{}, err os.Error) {
	var values interface{}

	if err = json.Unmarshal(body, &values); err != nil {
		return
	}
	data = values.(map[string]interface{})
	return
}

func fetchBody(method string) (body []byte, err os.Error) {
	resp, _, err := http.Get(GRAPHURL + method) // Response, final URL, error
	if err != nil {
		return
	}
	defer resp.Body.Close()

	body, err = ioutil.ReadAll(resp.Body)
	return
}
<commit_msg>Add missing values to Person, Connections and Page. Print out not implemented interface{}<commit_after>package facebook

import (
	"http"
	"io/ioutil"
	"json"
	"os"
	"strconv"
	"fmt"
)

const (
	GRAPHURL = "http://graph.facebook.com/"
)

type Metadata struct {
	Connections Connections
	// Name, Description
	Fields map[string]string
}

type Connections struct {
	Albums     string
	Statuses   string
	Links      string
	Posts      string
	Notes      string
	Videos     string
	Feed       string
	Photos     string
	Tagged     string
	Events     string
	Home       string
	Friends    string
	Activities string
	Interests  string
	Music      string
	Books      string
	Movies     string
	Television string
	Likes      string
	Groups     string
	InBox      string
	OutBox     string
	Updates    string
	Accounts   string
	CheckIns   string
	Picture    string
	Family     string
}

type Page struct {
	ID              string
	Name            string
	Picture         string
	Link            string
	Category        string
	Website         string
	Username        string
	Founded         string
	CompanyOverview string
	Mission         string
	Products        string
	FanCount        float64
}

func (p *Page) String() string {
	return "ID: " + p.ID + "\tName: " + p.Name + "\tPicture: " + p.Picture +
		"\tLink: " + p.Link + "\tCategory: " + p.Category + "\tWebsite: " +
		p.Website + "\tUsername: " + p.Username + "\tFounded: " + p.Founded +
		"\tCompany overview: " + p.CompanyOverview + "\tMission: " + p.Mission +
		"\tProducts: " + p.Products + "\tFan count:" +
		strconv.Ftoa64(p.FanCount, 'e', -1) + "\n"
}

type Person struct {
	ID              string
	Name            string
	FirstName       string
	LastName        string
	Link            string
	Gender          string
	Locale          string
	UpdatedTime     string
	Website         string
	Picture         string
	FanCount        float64
	Mission         string
	Category        string
	Username        string
	Products        string
	Founded         string
	CompanyOverview string
}

func (p *Person) String() string {
	return "ID: " + p.ID + "\tName: " + p.Name + "\tFirst name: " + p.FirstName +
		"\tLast name: " + p.LastName + "\tLink: " + p.Link + "\tGender: " +
		p.Gender + "\tLocale: " + p.Locale + "\tUpdated time: " + p.UpdatedTime +
		"\n"
}

func FetchPageIntrospect(id string) (page Page, err os.Error) {
	return FetchPage(id + "?metadata=1")
}

func FetchPage(id string) (page Page, err os.Error) {
	body, err := fetchBody(id)
	if err != nil {
		return
	}
	data, err := getJsonMap(body)
	if err != nil {
		return
	}
	for key, value := range data {
		switch key {
		case "website":
			page.Website = value.(string)
		case "picture":
			page.Picture = value.(string)
		case "fan_count":
			page.FanCount = value.(float64)
		case "mission":
			page.Mission = value.(string)
		case "category":
			page.Category = value.(string)
		case "name":
			page.Name = value.(string)
		case "username":
			page.Username = value.(string)
		case "link":
			page.Link = value.(string)
		case "id":
			page.ID = value.(string)
		case "products":
			page.Products = value.(string)
		case "founded":
			page.Founded = value.(string)
		case "company_overview":
			page.CompanyOverview = value.(string)
		case "type":
			// TODO: Look into type
		case "metadata":
			parseMetaData(value)
		default:
			debugInterface(value, key, "Page")
		}
	}
	return
}

func FetchPersonIntrospect(name string) (person Person, err os.Error) {
	return FetchPerson(name + "?metadata=1")
}

func FetchPerson(name string) (person Person, err os.Error) {
	body, err := fetchBody(name)
	if err != nil {
		return
	}
	data, err := getJsonMap(body)
	if err != nil {
		return
	}
	for key, value := range data {
		switch key {
		case "locale":
			person.Locale = value.(string)
		case "name":
			person.Name = value.(string)
		case "link":
			person.Link = value.(string)
		case "gender":
			person.Gender = value.(string)
		case "first_name":
			person.FirstName = value.(string)
		case "last_name":
			person.LastName = value.(string)
		case "id":
			person.ID = value.(string)
		case "website":
			person.Website = value.(string)
		case "picture":
			person.Picture = value.(string)
		case "mission":
			person.Mission = value.(string)
		case "category":
			person.Category = value.(string)
		case "username":
			person.Username = value.(string)
		case "products":
			person.Products = value.(string)
		case "founded":
			person.Founded = value.(string)
		case "company_overview":
			person.CompanyOverview = value.(string)
		case "fan_count":
			person.FanCount = value.(float64)
		case "type":
			// TODO: Look into type
		case "metadata":
			parseMetaData(value)
		default:
			debugInterface(value, key, "Person")
		}
	}
	return
}

func parseMetaData(value interface{}) (metadata Metadata) {
	data := value.(map[string]interface{})
	for key, v := range data {
		switch key {
		case "connections":
			metadata.Connections = parseConnections(v)
		case "fields":
			metadata.Fields = parseFields(v)
		default:
			debugInterface(v, key, "Metadata")
		}
	}
	return
}

func parseConnections(value interface{}) (connections Connections) {
	data := value.(map[string]interface{})
	for key, v := range data {
		switch key {
		case "feed":
			connections.Feed = v.(string)
		case "posts":
			connections.Posts = v.(string)
		case "tagged":
			connections.Tagged = v.(string)
		case "statuses":
			connections.Statuses = v.(string)
		case "links":
			connections.Links = v.(string)
		case "notes":
			connections.Notes = v.(string)
		case "photos":
			connections.Photos = v.(string)
		case "albums":
			connections.Albums = v.(string)
		case "events":
			connections.Events = v.(string)
		case "videos":
			connections.Videos = v.(string)
		case "home":
			connections.Home = v.(string)
		case "friends":
			connections.Home = v.(string)
		case "activities":
			connections.Activities = v.(string)
		case "interests":
			connections.Interests = v.(string)
		case "music":
			connections.Music = v.(string)
		case "books":
			connections.Books = v.(string)
		case "movies":
			connections.Movies = v.(string)
		case "television":
			connections.Television = v.(string)
		case "likes":
			connections.Likes = v.(string)
		case "groups":
			connections.Groups = v.(string)
		case "inbox":
			connections.InBox = v.(string)
		case "outbox":
			connections.OutBox = v.(string)
		case "updates":
			connections.Updates = v.(string)
		case "accounts":
			connections.Accounts = v.(string)
		case "checkins":
			connections.CheckIns = v.(string)
		case "picture":
			connections.Picture = v.(string)
		case "family":
			connections.Family = v.(string)
		default:
			debugInterface(v, key, "Connections")
		}
	}
	return
}

func parseFields(value interface{}) (fields map[string]string) {
	fields = make(map[string]string)
	var field map[string]interface{}
	data := value.([]interface{})
	for _, c := range data {
		field = c.(map[string]interface{})
		fields[field["name"].(string)] = field["description"].(string)
	}
	return
}

func getJsonMap(body []byte) (data map[string]interface{}, err os.Error) {
	var values interface{}

	if err = json.Unmarshal(body, &values); err != nil {
		return
	}
	data = values.(map[string]interface{})
	return
}

func fetchBody(method string) (body []byte, err os.Error) {
	resp, _, err := http.Get(GRAPHURL + method) // Response, final URL, error
	if err != nil {
		return
	}
	defer resp.Body.Close()

	body, err = ioutil.ReadAll(resp.Body)
	return
}

func debugInterface(value interface{}, key, funcName string) {
	var str string
	switch value.(type) {
	case float64:
		str = strconv.Ftoa64(value.(float64), 'e', -1)
	}
	fmt.Printf("%s: Unknown pair: %s : %s\n", funcName, key, str)
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package policy

import (
	"fmt"
	"io/ioutil"

	"k8s.io/apimachinery/pkg/runtime/schema"
	auditinternal "k8s.io/apiserver/pkg/apis/audit"
	auditv1alpha1 "k8s.io/apiserver/pkg/apis/audit/v1alpha1"
	auditv1beta1 "k8s.io/apiserver/pkg/apis/audit/v1beta1"
	"k8s.io/apiserver/pkg/apis/audit/validation"
	"k8s.io/apiserver/pkg/audit"

	"github.com/golang/glog"
)

var (
	apiGroupVersions = []schema.GroupVersion{
		auditv1beta1.SchemeGroupVersion,
		auditv1alpha1.SchemeGroupVersion,
	}
	apiGroupVersionSet = map[schema.GroupVersion]bool{}
)

func init() {
	for _, gv := range apiGroupVersions {
		apiGroupVersionSet[gv] = true
	}
}

func LoadPolicyFromFile(filePath string) (*auditinternal.Policy, error) {
	if filePath == "" {
		return nil, fmt.Errorf("file path not specified")
	}
	policyDef, err := ioutil.ReadFile(filePath)
	if err != nil {
		return nil, fmt.Errorf("failed to read file path %q: %+v", filePath, err)
	}

	policy := &auditinternal.Policy{}
	decoder := audit.Codecs.UniversalDecoder(apiGroupVersions...)

	_, gvk, err := decoder.Decode(policyDef, nil, policy)
	if err != nil {
		return nil, fmt.Errorf("failed decoding file %q: %v", filePath, err)
	}

	// Ensure the policy file contained an apiVersion and kind.
	if !apiGroupVersionSet[schema.GroupVersion{Group: gvk.Group, Version: gvk.Version}] {
		return nil, fmt.Errorf("unknown group version field %v in policy file %s", gvk, filePath)
	}

	if err := validation.ValidatePolicy(policy); err != nil {
		return nil, err.ToAggregate()
	}

	policyCnt := len(policy.Rules)
	if policyCnt == 0 {
		return nil, fmt.Errorf("loaded illegal policy with 0 rules from file %s", filePath)
	}
	glog.V(4).Infof("Loaded %d audit policy rules from file %s", policyCnt, filePath)
	return policy, nil
}
<commit_msg>UPSTREAM: 68632: allow audit policy to be loaded from any byte source<commit_after>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package policy

import (
	"fmt"
	"io/ioutil"

	"k8s.io/apimachinery/pkg/runtime/schema"
	auditinternal "k8s.io/apiserver/pkg/apis/audit"
	auditv1alpha1 "k8s.io/apiserver/pkg/apis/audit/v1alpha1"
	auditv1beta1 "k8s.io/apiserver/pkg/apis/audit/v1beta1"
	"k8s.io/apiserver/pkg/apis/audit/validation"
	"k8s.io/apiserver/pkg/audit"

	"github.com/golang/glog"
)

var (
	apiGroupVersions = []schema.GroupVersion{
		auditv1beta1.SchemeGroupVersion,
		auditv1alpha1.SchemeGroupVersion,
	}
	apiGroupVersionSet = map[schema.GroupVersion]bool{}
)

func init() {
	for _, gv := range apiGroupVersions {
		apiGroupVersionSet[gv] = true
	}
}

func LoadPolicyFromFile(filePath string) (*auditinternal.Policy, error) {
	if filePath == "" {
		return nil, fmt.Errorf("file path not specified")
	}
	policyDef, err := ioutil.ReadFile(filePath)
	if err != nil {
		return nil, fmt.Errorf("failed to read file path %q: %+v", filePath, err)
	}

	return LoadPolicyFromBytes(policyDef, filePath)
}

func LoadPolicyFromBytes(policyDef []byte, location string) (*auditinternal.Policy, error) {
	policy := &auditinternal.Policy{}
	decoder := audit.Codecs.UniversalDecoder(apiGroupVersions...)

	_, gvk, err := decoder.Decode(policyDef, nil, policy)
	if err != nil {
		return nil, fmt.Errorf("failed decoding file %q: %v", location, err)
	}

	// Ensure the policy file contained an apiVersion and kind.
	if !apiGroupVersionSet[schema.GroupVersion{Group: gvk.Group, Version: gvk.Version}] {
		return nil, fmt.Errorf("unknown group version field %v in policy file %s", gvk, location)
	}

	if err := validation.ValidatePolicy(policy); err != nil {
		return nil, err.ToAggregate()
	}

	policyCnt := len(policy.Rules)
	if policyCnt == 0 {
		return nil, fmt.Errorf("loaded illegal policy with 0 rules from file %s", location)
	}
	glog.V(4).Infof("Loaded %d audit policy rules from file %s", policyCnt, location)
	return policy, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"code.google.com/p/go-charset/charset"
	_ "code.google.com/p/go-charset/data"
	"encoding/xml"
	"io/ioutil"
	"log"
	"net/http"
)

type Piece struct {
	Title    string
	Composer string
}

type SecondInversionFeed struct {
	XMLName xml.Name             `xml:nexgen_audio_export`
	Audio   SecondInversionAudio `xml:"audio"`
}

type SecondInversionAudio struct {
	Title    string `xml:"title"`
	Composer string `xml:"composer"`
}

func checkErr(err error, msg string) {
	if err != nil {
		log.Fatalln(msg, err)
	}
}

func translateSecondInversion(data []byte) Piece {
	var feed SecondInversionFeed
	reader := bytes.NewReader(data)
	decoder := xml.NewDecoder(reader)
	decoder.CharsetReader = charset.NewReader
	err := decoder.Decode(&feed)
	checkErr(err, "translateSecondInversion decode error")
	return Piece{feed.Audio.Title, feed.Audio.Composer}
}

func secondInversion() Piece {
	resp, err := http.Get(secondInversionUrl)
	checkErr(err, "failed get of secondInversion feed")
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	checkErr(err, "failed to read respsone body")
	return translateSecondInversion(body)
}
<commit_msg>our jQuery expects lowercase keys<commit_after>package main

import (
	"bytes"
	"code.google.com/p/go-charset/charset"
	_ "code.google.com/p/go-charset/data"
	"encoding/xml"
	"io/ioutil"
	"log"
	"net/http"
)

type Piece struct {
	Title    string `json:"title"`
	Composer string `json:"composer"`
}

type SecondInversionFeed struct {
	XMLName xml.Name             `xml:nexgen_audio_export`
	Audio   SecondInversionAudio `xml:"audio"`
}

type SecondInversionAudio struct {
	Title    string `xml:"title"`
	Composer string `xml:"composer"`
}

func checkErr(err error, msg string) {
	if err != nil {
		log.Fatalln(msg, err)
	}
}

func translateSecondInversion(data []byte) Piece {
	var feed SecondInversionFeed
	reader := bytes.NewReader(data)
	decoder := xml.NewDecoder(reader)
	decoder.CharsetReader = charset.NewReader
	err := decoder.Decode(&feed)
	checkErr(err, "translateSecondInversion decode error")
	return Piece{feed.Audio.Title, feed.Audio.Composer}
}

func secondInversion() Piece {
	resp, err := http.Get(secondInversionUrl)
	checkErr(err, "failed get of secondInversion feed")
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	checkErr(err, "failed to read respsone body")
	return translateSecondInversion(body)
}
<|endoftext|>


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

package ksr

import (
	"sync"
	"testing"
	"time"

	"github.com/onsi/gomega"

	coreV1 "k8s.io/api/core/v1"
	"k8s.io/client-go/kubernetes"

	proto "github.com/contiv/vpp/plugins/ksr/model/namespace"
	"github.com/ligato/cn-infra/flavors/local"
)

type NamespaceTestVars struct {
	k8sListWatch *mockK8sListWatch
	mockKvWriter *mockKeyProtoValWriter
	mockKvLister *mockKeyProtoValLister
	nsReflector  *NamespaceReflector
	svc          *coreV1.Service
	svcTestData  []coreV1.Service
}

var nsTestVars NamespaceTestVars

func TestNamespaceReflector(t *testing.T) {
	gomega.RegisterTestingT(t)

	flavorLocal := &local.FlavorLocal{}
	flavorLocal.Inject()

	nsTestVars.k8sListWatch = &mockK8sListWatch{}
	nsTestVars.mockKvWriter = newMockKeyProtoValWriter()
	nsTestVars.mockKvLister = newMockKeyProtoValLister(nsTestVars.mockKvWriter.ds)

	nsTestVars.nsReflector = &NamespaceReflector{
		Reflector: Reflector{
			Log:          flavorLocal.LoggerFor("namespace-reflector"),
			K8sClientset: &kubernetes.Clientset{},
			K8sListWatch: nsTestVars.k8sListWatch,
			Writer:       nsTestVars.mockKvWriter,
			Lister:       nsTestVars.mockKvLister,
			dsSynced:     false,
			objType:      "Service",
		},
	}

	stopCh := make(chan struct{})
	var wg sync.WaitGroup
	err := nsTestVars.nsReflector.Init(stopCh, &wg)
	gomega.Expect(err).To(gomega.BeNil())

	// Wait for the initial sync to finish
	for {
		if nsTestVars.nsReflector.HasSynced() {
			break
		}
		time.Sleep(time.Millisecond * 100)
	}

	t.Run("newNamespace", testNewNamespace)
	nsTestVars.mockKvWriter.ClearDs()
	// TODO: add more
}

func testNewNamespace(t *testing.T) {
	ns := &coreV1.Namespace{}
	ns.Name = "namespace1"
	ns.Labels = make(map[string]string)
	ns.Labels["role"] = "mgmt"
	ns.Labels["privileged"] = "true"
	nsTestVars.k8sListWatch.Add(ns)

	nsProto := &proto.Namespace{}
	err := nsTestVars.mockKvWriter.GetValue(proto.Key(ns.GetName()), nsProto)
	gomega.Expect(err).To(gomega.BeNil())
	gomega.Expect(nsProto).NotTo(gomega.BeNil())
	gomega.Expect(nsProto.Name).To(gomega.Equal(ns.GetName()))
	gomega.Expect(nsProto.Label).To(gomega.HaveLen(2))
	gomega.Expect(nsProto.Label).To(gomega.ContainElement(&proto.Namespace_Label{Key: "role", Value: "mgmt"}))
	gomega.Expect(nsProto.Label).To(gomega.ContainElement(&proto.Namespace_Label{Key: "privileged", Value: "true"}))
}
<commit_msg>Updated namespace reflector test<commit_after>// Copyright (c) 2018 Cisco and/or its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ksr

import (
	"sync"
	"testing"
	"time"

	"github.com/onsi/gomega"

	coreV1 "k8s.io/api/core/v1"
	"k8s.io/client-go/kubernetes"

	proto "github.com/contiv/vpp/plugins/ksr/model/namespace"
	"github.com/ligato/cn-infra/flavors/local"
)

type NamespaceTestVars struct {
	k8sListWatch *mockK8sListWatch
	mockKvWriter *mockKeyProtoValWriter
	mockKvLister *mockKeyProtoValLister
	nsReflector  *NamespaceReflector
	svc          *coreV1.Service
	svcTestData  []coreV1.Service
}

var nsTestVars NamespaceTestVars

func TestNamespaceReflector(t *testing.T) {
	gomega.RegisterTestingT(t)

	flavorLocal := &local.FlavorLocal{}
	flavorLocal.Inject()

	nsTestVars.k8sListWatch = &mockK8sListWatch{}
	nsTestVars.mockKvWriter = newMockKeyProtoValWriter()
	nsTestVars.mockKvLister = newMockKeyProtoValLister(nsTestVars.mockKvWriter.ds)

	nsTestVars.nsReflector = &NamespaceReflector{
		Reflector: Reflector{
			Log:          flavorLocal.LoggerFor("namespace-reflector"),
			K8sClientset: &kubernetes.Clientset{},
			K8sListWatch: nsTestVars.k8sListWatch,
			Writer:       nsTestVars.mockKvWriter,
			Lister:       nsTestVars.mockKvLister,
			dsSynced:     false,
			objType:      "Service",
		},
	}

	stopCh := make(chan struct{})
	var wg sync.WaitGroup
	err := nsTestVars.nsReflector.Init(stopCh, &wg)
	gomega.Expect(err).To(gomega.BeNil())

	// Wait for the initial sync to finish
	for {
		if nsTestVars.nsReflector.HasSynced() {
			break
		}
		time.Sleep(time.Millisecond * 100)
	}

	t.Run("addDeleteNamespace", testAddDeleteNamespace)
	nsTestVars.mockKvWriter.ClearDs()
	t.Run("updateNamespace", testUpdateeNamespace)
}

func testAddDeleteNamespace(t *testing.T) {

	ns := &coreV1.Namespace{}
	ns.Name = "namespace1"
	ns.Labels = make(map[string]string)
	ns.Labels["role"] = "mgmt"
	ns.Labels["privileged"] = "true"

	adds := nsTestVars.nsReflector.GetStats().NumAdds
	argErrs := nsTestVars.nsReflector.GetStats().NumArgErrors

	// Test bad argument type in Add
	nsTestVars.k8sListWatch.Add(&ns)

	gomega.Expect(argErrs + 1).To(gomega.Equal(nsTestVars.nsReflector.GetStats().NumArgErrors))
	gomega.Expect(adds).To(gomega.Equal(nsTestVars.nsReflector.GetStats().NumAdds))

	nsTestVars.k8sListWatch.Add(ns)

	nsProto := &proto.Namespace{}
	err := nsTestVars.mockKvWriter.GetValue(proto.Key(ns.GetName()), nsProto)
	gomega.Expect(err).To(gomega.BeNil())
	gomega.Expect(nsProto).NotTo(gomega.BeNil())
	gomega.Expect(nsProto.Name).To(gomega.Equal(ns.GetName()))
	gomega.Expect(nsProto.Label).To(gomega.HaveLen(2))
	gomega.Expect(nsProto.Label).To(gomega.ContainElement(&proto.Namespace_Label{Key: "role", Value: "mgmt"}))
	gomega.Expect(nsProto.Label).To(gomega.ContainElement(&proto.Namespace_Label{Key: "privileged", Value: "true"}))

	gomega.Expect(adds + 1).To(gomega.Equal(nsTestVars.nsReflector.GetStats().NumAdds))

	// Test a bad argument type in Delete
	dels := nsTestVars.nsReflector.GetStats().NumDeletes
	argErrs = nsTestVars.nsReflector.GetStats().NumArgErrors

	nsTestVars.k8sListWatch.Delete(&ns)

	// Wrong data type in argument: NumArgErrors stat should roll and the
	// delete should not happen
	gomega.Expect(argErrs + 1).To(gomega.Equal(nsTestVars.nsReflector.GetStats().NumArgErrors))
	gomega.Expect(dels).To(gomega.Equal(nsTestVars.nsReflector.GetStats().NumDeletes))
	gomega.Expect(len(nsTestVars.mockKvWriter.ds)).Should(gomega.BeNumerically("==", 1))

	// Test delete
	nsTestVars.k8sListWatch.Delete(ns)

	// NumArgErrors stat should roll and the data store should be empty
	gomega.Expect(dels + 1).To(gomega.Equal(nsTestVars.nsReflector.GetStats().NumDeletes))
	gomega.Expect(len(nsTestVars.mockKvWriter.ds)).Should(gomega.BeNumerically("==", 0))
}

func testUpdateeNamespace(t *testing.T) {

	nsOld := &coreV1.Namespace{}
	nsOld.Name = "namespace1"
	nsOld.Labels = make(map[string]string)
	nsOld.Labels["role"] = "mgmt"
	nsOld.Labels["privileged"] = "true"

	adds := nsTestVars.nsReflector.GetStats().NumAdds

	nsTestVars.k8sListWatch.Add(nsOld)

	nsProtoOld := &proto.Namespace{}
	err := nsTestVars.mockKvWriter.GetValue(proto.Key(nsOld.GetName()), nsProtoOld)
	gomega.Expect(err).To(gomega.BeNil())
	gomega.Expect(nsProtoOld).NotTo(gomega.BeNil())
	gomega.Expect(nsProtoOld.Name).To(gomega.Equal(nsOld.GetName()))
	gomega.Expect(nsProtoOld.Label).To(gomega.HaveLen(2))
	gomega.Expect(nsProtoOld.Label).To(gomega.ContainElement(&proto.Namespace_Label{Key: "role", Value: "mgmt"}))
	gomega.Expect(nsProtoOld.Label).To(gomega.ContainElement(&proto.Namespace_Label{Key: "privileged", Value: "true"}))

	gomega.Expect(adds + 1).To(gomega.Equal(nsTestVars.nsReflector.GetStats().NumAdds))

	nsNew := *nsOld
	nsNew.Labels["privileged"] = "false"
	updates := nsTestVars.nsReflector.GetStats().NumUpdates

	// Test update
	nsTestVars.k8sListWatch.Update(nsOld, &nsNew)

	gomega.Expect(updates + 1).To(gomega.Equal(nsTestVars.nsReflector.GetStats().NumUpdates))

	nsProtoNew := &proto.Namespace{}
	err = nsTestVars.mockKvWriter.GetValue(proto.Key(nsOld.GetName()), nsProtoNew)
	gomega.Expect(err).To(gomega.BeNil())

	gomega.Expect(nsProtoOld.Label).To(gomega.ContainElement(&proto.Namespace_Label{Key: "privileged", Value: "true"}))
	gomega.Expect(nsProtoNew.Label).To(gomega.ContainElement(&proto.Namespace_Label{Key: "privileged", Value: "false"}))

}

<|endoftext|>


<commit_before>package portallocator

import (
	"bufio"
	"errors"
	"fmt"
	"net"
	"os"
	"sync"

	"github.com/Sirupsen/logrus"
)

const (
	// DefaultPortRangeStart indicates the first port in port range
	DefaultPortRangeStart = 49153
	// DefaultPortRangeEnd indicates the last port in port range
	DefaultPortRangeEnd = 65535
)

type ipMapping map[string]protoMap

var (
	// ErrAllPortsAllocated is returned when no more ports are available
	ErrAllPortsAllocated = errors.New("all ports are allocated")
	// ErrUnknownProtocol is returned when an unknown protocol was specified
	ErrUnknownProtocol = errors.New("unknown protocol")
	defaultIP          = net.ParseIP("0.0.0.0")
	once               sync.Once
	instance           *PortAllocator
	createInstance     = func() { instance = newInstance() }
)

// ErrPortAlreadyAllocated is the returned error information when a requested port is already being used
type ErrPortAlreadyAllocated struct {
	ip   string
	port int
}

func newErrPortAlreadyAllocated(ip string, port int) ErrPortAlreadyAllocated {
	return ErrPortAlreadyAllocated{
		ip:   ip,
		port: port,
	}
}

// IP returns the address to which the used port is associated
func (e ErrPortAlreadyAllocated) IP() string {
	return e.ip
}

// Port returns the value of the already used port
func (e ErrPortAlreadyAllocated) Port() int {
	return e.port
}

// IPPort returns the address and the port in the form ip:port
func (e ErrPortAlreadyAllocated) IPPort() string {
	return fmt.Sprintf("%s:%d", e.ip, e.port)
}

// Error is the implementation of error.Error interface
func (e ErrPortAlreadyAllocated) Error() string {
	return fmt.Sprintf("Bind for %s:%d failed: port is already allocated", e.ip, e.port)
}

type (
	// PortAllocator manages the transport ports database
	PortAllocator struct {
		mutex sync.Mutex
		ipMap ipMapping
		Begin int
		End   int
	}
	portMap struct {
		p          map[int]struct{}
		begin, end int
		last       int
	}
	protoMap map[string]*portMap
)

// Get returns the default instance of PortAllocator
func Get() *PortAllocator {
	// Port Allocator is a singleton
	// Note: Long term solution will be each PortAllocator will have access to
	// the OS so that it can have up to date view of the OS port allocation.
	// When this happens singleton behavior will be removed. Clients do not
	// need to worry about this, they will not see a change in behavior.
	once.Do(createInstance)
	return instance
}

func newInstance() *PortAllocator {
	start, end, err := getDynamicPortRange()
	if err != nil {
		logrus.Warn(err)
		start, end = DefaultPortRangeStart, DefaultPortRangeEnd
	}
	return &PortAllocator{
		ipMap: ipMapping{},
		Begin: start,
		End:   end,
	}
}

func getDynamicPortRange() (start int, end int, err error) {
	const portRangeKernelParam = "/proc/sys/net/ipv4/ip_local_port_range"
	portRangeFallback := fmt.Sprintf("using fallback port range %d-%d", DefaultPortRangeStart, DefaultPortRangeEnd)
	file, err := os.Open(portRangeKernelParam)
	if err != nil {
		return 0, 0, fmt.Errorf("port allocator - %s due to error: %v", portRangeFallback, err)
	}
	n, err := fmt.Fscanf(bufio.NewReader(file), "%d\t%d", &start, &end)
	if n != 2 || err != nil {
		if err == nil {
			err = fmt.Errorf("unexpected count of parsed numbers (%d)", n)
		}
		return 0, 0, fmt.Errorf("port allocator - failed to parse system ephemeral port range from %s - %s: %v", portRangeKernelParam, portRangeFallback, err)
	}
	return start, end, nil
}

// RequestPort requests new port from global ports pool for specified ip and proto.
// If port is 0 it returns first free port. Otherwise it checks port availability
// in pool and return that port or error if port is already busy.
func (p *PortAllocator) RequestPort(ip net.IP, proto string, port int) (int, error) {
	p.mutex.Lock()
	defer p.mutex.Unlock()

	if proto != "tcp" && proto != "udp" {
		return 0, ErrUnknownProtocol
	}

	if ip == nil {
		ip = defaultIP
	}
	ipstr := ip.String()
	protomap, ok := p.ipMap[ipstr]
	if !ok {
		protomap = protoMap{
			"tcp": p.newPortMap(),
			"udp": p.newPortMap(),
		}

		p.ipMap[ipstr] = protomap
	}
	mapping := protomap[proto]
	if port > 0 {
		if _, ok := mapping.p[port]; !ok {
			mapping.p[port] = struct{}{}
			return port, nil
		}
		return 0, newErrPortAlreadyAllocated(ipstr, port)
	}

	port, err := mapping.findPort()
	if err != nil {
		return 0, err
	}
	return port, nil
}

// ReleasePort releases port from global ports pool for specified ip and proto.
func (p *PortAllocator) ReleasePort(ip net.IP, proto string, port int) error {
	p.mutex.Lock()
	defer p.mutex.Unlock()

	if ip == nil {
		ip = defaultIP
	}
	protomap, ok := p.ipMap[ip.String()]
	if !ok {
		return nil
	}
	delete(protomap[proto].p, port)
	return nil
}

func (p *PortAllocator) newPortMap() *portMap {
	return &portMap{
		p:     map[int]struct{}{},
		begin: p.Begin,
		end:   p.End,
		last:  p.End,
	}
}

// ReleaseAll releases all ports for all ips.
func (p *PortAllocator) ReleaseAll() error {
	p.mutex.Lock()
	p.ipMap = ipMapping{}
	p.mutex.Unlock()
	return nil
}

func (pm *portMap) findPort() (int, error) {
	port := pm.last
	for i := 0; i <= pm.end-pm.begin; i++ {
		port++
		if port > pm.end {
			port = pm.begin
		}

		if _, ok := pm.p[port]; !ok {
			pm.p[port] = struct{}{}
			pm.last = port
			return port, nil
		}
	}
	return 0, ErrAllPortsAllocated
}
<commit_msg>Do not warn in packages<commit_after>package portallocator

import (
	"bufio"
	"errors"
	"fmt"
	"net"
	"os"
	"sync"
)

const (
	// DefaultPortRangeStart indicates the first port in port range
	DefaultPortRangeStart = 49153
	// DefaultPortRangeEnd indicates the last port in port range
	DefaultPortRangeEnd = 65535
)

type ipMapping map[string]protoMap

var (
	// ErrAllPortsAllocated is returned when no more ports are available
	ErrAllPortsAllocated = errors.New("all ports are allocated")
	// ErrUnknownProtocol is returned when an unknown protocol was specified
	ErrUnknownProtocol = errors.New("unknown protocol")
	defaultIP          = net.ParseIP("0.0.0.0")
	once               sync.Once
	instance           *PortAllocator
	createInstance     = func() { instance = newInstance() }
)

// ErrPortAlreadyAllocated is the returned error information when a requested port is already being used
type ErrPortAlreadyAllocated struct {
	ip   string
	port int
}

func newErrPortAlreadyAllocated(ip string, port int) ErrPortAlreadyAllocated {
	return ErrPortAlreadyAllocated{
		ip:   ip,
		port: port,
	}
}

// IP returns the address to which the used port is associated
func (e ErrPortAlreadyAllocated) IP() string {
	return e.ip
}

// Port returns the value of the already used port
func (e ErrPortAlreadyAllocated) Port() int {
	return e.port
}

// IPPort returns the address and the port in the form ip:port
func (e ErrPortAlreadyAllocated) IPPort() string {
	return fmt.Sprintf("%s:%d", e.ip, e.port)
}

// Error is the implementation of error.Error interface
func (e ErrPortAlreadyAllocated) Error() string {
	return fmt.Sprintf("Bind for %s:%d failed: port is already allocated", e.ip, e.port)
}

type (
	// PortAllocator manages the transport ports database
	PortAllocator struct {
		mutex sync.Mutex
		ipMap ipMapping
		Begin int
		End   int
	}
	portMap struct {
		p          map[int]struct{}
		begin, end int
		last       int
	}
	protoMap map[string]*portMap
)

// Get returns the default instance of PortAllocator
func Get() *PortAllocator {
	// Port Allocator is a singleton
	// Note: Long term solution will be each PortAllocator will have access to
	// the OS so that it can have up to date view of the OS port allocation.
	// When this happens singleton behavior will be removed. Clients do not
	// need to worry about this, they will not see a change in behavior.
	once.Do(createInstance)
	return instance
}

func newInstance() *PortAllocator {
	start, end, err := getDynamicPortRange()
	if err != nil {
		start, end = DefaultPortRangeStart, DefaultPortRangeEnd
	}
	return &PortAllocator{
		ipMap: ipMapping{},
		Begin: start,
		End:   end,
	}
}

func getDynamicPortRange() (start int, end int, err error) {
	const portRangeKernelParam = "/proc/sys/net/ipv4/ip_local_port_range"
	portRangeFallback := fmt.Sprintf("using fallback port range %d-%d", DefaultPortRangeStart, DefaultPortRangeEnd)
	file, err := os.Open(portRangeKernelParam)
	if err != nil {
		return 0, 0, fmt.Errorf("port allocator - %s due to error: %v", portRangeFallback, err)
	}
	n, err := fmt.Fscanf(bufio.NewReader(file), "%d\t%d", &start, &end)
	if n != 2 || err != nil {
		if err == nil {
			err = fmt.Errorf("unexpected count of parsed numbers (%d)", n)
		}
		return 0, 0, fmt.Errorf("port allocator - failed to parse system ephemeral port range from %s - %s: %v", portRangeKernelParam, portRangeFallback, err)
	}
	return start, end, nil
}

// RequestPort requests new port from global ports pool for specified ip and proto.
// If port is 0 it returns first free port. Otherwise it checks port availability
// in pool and return that port or error if port is already busy.
func (p *PortAllocator) RequestPort(ip net.IP, proto string, port int) (int, error) {
	p.mutex.Lock()
	defer p.mutex.Unlock()

	if proto != "tcp" && proto != "udp" {
		return 0, ErrUnknownProtocol
	}

	if ip == nil {
		ip = defaultIP
	}
	ipstr := ip.String()
	protomap, ok := p.ipMap[ipstr]
	if !ok {
		protomap = protoMap{
			"tcp": p.newPortMap(),
			"udp": p.newPortMap(),
		}

		p.ipMap[ipstr] = protomap
	}
	mapping := protomap[proto]
	if port > 0 {
		if _, ok := mapping.p[port]; !ok {
			mapping.p[port] = struct{}{}
			return port, nil
		}
		return 0, newErrPortAlreadyAllocated(ipstr, port)
	}

	port, err := mapping.findPort()
	if err != nil {
		return 0, err
	}
	return port, nil
}

// ReleasePort releases port from global ports pool for specified ip and proto.
func (p *PortAllocator) ReleasePort(ip net.IP, proto string, port int) error {
	p.mutex.Lock()
	defer p.mutex.Unlock()

	if ip == nil {
		ip = defaultIP
	}
	protomap, ok := p.ipMap[ip.String()]
	if !ok {
		return nil
	}
	delete(protomap[proto].p, port)
	return nil
}

func (p *PortAllocator) newPortMap() *portMap {
	return &portMap{
		p:     map[int]struct{}{},
		begin: p.Begin,
		end:   p.End,
		last:  p.End,
	}
}

// ReleaseAll releases all ports for all ips.
func (p *PortAllocator) ReleaseAll() error {
	p.mutex.Lock()
	p.ipMap = ipMapping{}
	p.mutex.Unlock()
	return nil
}

func (pm *portMap) findPort() (int, error) {
	port := pm.last
	for i := 0; i <= pm.end-pm.begin; i++ {
		port++
		if port > pm.end {
			port = pm.begin
		}

		if _, ok := pm.p[port]; !ok {
			pm.p[port] = struct{}{}
			pm.last = port
			return port, nil
		}
	}
	return 0, ErrAllPortsAllocated
}
<|endoftext|>


<commit_before>package vm

type List struct {
	first *Thunk
	rest  *Thunk
}

var emptyList = List{nil, nil}

func NewList(ts ...*Thunk) List {
	return prepend(append(ts, Normal(emptyList))...).(List)
}

func ListThunk(ts ...*Thunk) *Thunk {
	return Normal(NewList(ts...))
}

func (l1 List) Equal(e Equalable) Object {
	l2 := e.(List)

	if l1 == emptyList || l2 == emptyList {
		return l1 == l2
	}

	var bs [2]Bool

	for i, o := range []Object{
		Equal(l1.first, l2.first),
		Equal(l1.rest, l2.rest),
	} {
		b, ok := o.(Bool)

		if !ok {
			return notBoolError(o)
		}

		bs[i] = b
	}

	return bs[0] && bs[1]
}

var Prepend = NewLazyFunction(prepend)

func prepend(ts ...*Thunk) Object {
	switch len(ts) {
	case 0:
		return NumArgsError("prepend", "> 1")
	case 1:
		return ts[0]
	}

	last := len(ts) - 1
	l := cons(ts[last-1], ts[last])

	for i := last - 2; i >= 0; i-- {
		l = cons(ts[i], Normal(l))
	}

	return l
}

func cons(t1, t2 *Thunk) List {
	return List{t1, t2}
}

var First = NewStrictFunction(first)

func first(os ...Object) Object {
	if len(os) != 1 {
		return NumArgsError("first", "1")
	}

	o := os[0]
	l, ok := o.(List)

	if !ok {
		return notListError(o)
	} else if l == emptyList {
		return emptyListError()
	}

	return l.first.Eval()
}

var Rest = NewStrictFunction(rest)

func rest(os ...Object) Object {
	if len(os) != 1 {
		return NumArgsError("rest", "1")
	}

	o := os[0]
	l, ok := o.(List)

	if !ok {
		return notListError(o)
	} else if l == emptyList {
		return emptyListError()
	}

	return l.rest.Eval()
}

func notListError(o Object) Error {
	return TypeError(o, "List")
}

func emptyListError() Error {
	return ValueError("The list is empty. You cannot apply rest.")
}
<commit_msg>Create short circuit in List.Equal()<commit_after>package vm

type List struct {
	first *Thunk
	rest  *Thunk
}

var emptyList = List{nil, nil}

func NewList(ts ...*Thunk) List {
	return prepend(append(ts, Normal(emptyList))...).(List)
}

func ListThunk(ts ...*Thunk) *Thunk {
	return Normal(NewList(ts...))
}

func (l1 List) Equal(e Equalable) Object {
	l2 := e.(List)

	if l1 == emptyList || l2 == emptyList {
		return l1 == l2
	}

	for _, o := range []Object{
		// Don't evaluate these parallely for short circuit behavior.
		Equal(l1.first, l2.first),
		Equal(l1.rest, l2.rest),
	} {
		b, ok := o.(Bool)

		if !ok {
			return notBoolError(o)
		} else if !b {
			return False
		}
	}

	return True
}

var Prepend = NewLazyFunction(prepend)

func prepend(ts ...*Thunk) Object {
	switch len(ts) {
	case 0:
		return NumArgsError("prepend", "> 1")
	case 1:
		return ts[0]
	}

	last := len(ts) - 1
	l := cons(ts[last-1], ts[last])

	for i := last - 2; i >= 0; i-- {
		l = cons(ts[i], Normal(l))
	}

	return l
}

func cons(t1, t2 *Thunk) List {
	return List{t1, t2}
}

var First = NewStrictFunction(first)

func first(os ...Object) Object {
	if len(os) != 1 {
		return NumArgsError("first", "1")
	}

	o := os[0]
	l, ok := o.(List)

	if !ok {
		return notListError(o)
	} else if l == emptyList {
		return emptyListError()
	}

	return l.first.Eval()
}

var Rest = NewStrictFunction(rest)

func rest(os ...Object) Object {
	if len(os) != 1 {
		return NumArgsError("rest", "1")
	}

	o := os[0]
	l, ok := o.(List)

	if !ok {
		return notListError(o)
	} else if l == emptyList {
		return emptyListError()
	}

	return l.rest.Eval()
}

func notListError(o Object) Error {
	return TypeError(o, "List")
}

func emptyListError() Error {
	return ValueError("The list is empty. You cannot apply rest.")
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"log"
	"mcquery"
	"net/http"
	"strconv"
	"strings"
)

func parseCommand(cmd string) (string, uint16, error) {
	args := strings.Fields(cmd)
	if len(args) == 0 {
		return "", 0, errors.New("Your request was malformed.")
	} else if len(args) == 1 {
		return args[0], 25565, nil
	} else {
		port, err := strconv.ParseUint(args[1], 10, 16)
		if err != nil {
			return "", 0, errors.New("Your request was malformed.")
		}
		return args[0], uint16(port), nil
	}
}

func handleCommand(w http.ResponseWriter, r *http.Request) {

	ip, port, err := parseCommand(r.FormValue("text"))

	if err != nil {
		log.Println(err.Error())
		w.WriteHeader(400)
		w.Write([]byte(err.Error()))
		return
	}

	// TODO consolidate all these steps in package mcquery
	rw, err, kill := mcquery.Connect(ip, port)
	if err != nil {
		log.Println(err.Error())
		w.WriteHeader(400)
		w.Write([]byte("An error occurred while connecting to the MC server."))
		kill <- true
		return
	}
	challenge, err := mcquery.Handshake(rw)
	if err != nil {
		log.Println(err.Error())
		w.WriteHeader(400)
		w.Write([]byte("An error occurred while handshaking with MC server."))
		kill <- true
		return
	}
	statResponse, err := mcquery.BasicStat(rw, challenge)
	if err != nil {
		log.Println(err.Error())
		w.WriteHeader(400)
		w.Write([]byte("An error occurred while reading status of MC server."))
		kill <- true
		return
	}
	kill <- true

	responseMap := make(map[string]interface{})
	responseString := fmt.Sprintf("```MOTD: %s\n", statResponse.Motd)
	responseString += fmt.Sprintf("Gametype: %s\n", statResponse.Gametype)
	responseString += fmt.Sprintf("Map: %s\n", statResponse.Map)
	responseString += fmt.Sprintf("NumPlayers: %s\n", statResponse.NumPlayers)
	responseString += fmt.Sprintf("MaxPlayers: %s\n", statResponse.MaxPlayers)
	responseString += fmt.Sprintf("HostPort: %d\n", statResponse.HostPort)
	responseString += fmt.Sprintf("HostIp: %s\n```", statResponse.HostIp)
	responseMap["text"] = responseString
	responseMap["response_type"] = "in_channel"
	data, err := json.Marshal(responseMap)

	if err != nil {
		log.Println(err.Error())
		w.WriteHeader(400)
		w.Write([]byte("Internal JSON Error Ocurred."))
		return
	}

	w.Header().Set("content-type", "application/json")
	w.WriteHeader(200)
	w.Write(data)

}

func main() {

  fmt.Printf("Starting mcbot server...\n")

	port := flag.String("port", "80", "Port to bind to")
	flag.Parse()

	http.HandleFunc("/", handleCommand)

  fmt.Printf("Port is %s\n", *port)

	err := http.ListenAndServe(fmt.Sprintf(":%s", *port), nil)

	if err != nil {
		panic(err)
	}

}
<commit_msg>Change command syntax and add configurations<commit_after>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"log"
	"math"
	"mcquery"
	"net/http"
	"os"
	"strings"
)

type Configuration struct {
	HiddenByDefault bool
	DefaultPort     uint16
	DefaultIp       string
}

var globalConfiguration Configuration

func getConfiguration(filename string) Configuration {
	var cfg Configuration
	if filename == "" {
		log.Println("No configuration file, using default")
		return Configuration{
			false,
			25565, // default minecraft port
			"",
		}
	}

	file, err := os.Open(filename)
	if err != nil {
		log.Fatal(err.Error())
	}

	decoder := json.NewDecoder(file)
	err = decoder.Decode(&cfg)
	if err != nil {
		log.Fatal(err.Error())
	}
	return cfg
}

type Command struct {
	Ip     string
	Port   uint16
	Hidden bool // Whether every use can see output or not
}

func parseCommand(cmd string, cfg Configuration) (*Command, error) {
	flagSet := flag.NewFlagSet("command", flag.ContinueOnError)
	command := new(Command)

	flagSet.BoolVar(&command.Hidden, "hidden", cfg.HiddenByDefault,
		"Determines if command output visible to everyone")

	flagSet.StringVar(&command.Ip, "ip", cfg.DefaultIp, "Minecraft server IP")

	var portNum uint64
	flagSet.Uint64Var(&portNum, "port", uint64(cfg.DefaultPort), "MC Server Port")

	err := flagSet.Parse(strings.Fields(cmd))
	if err != nil {
		return nil, err
	}
	if portNum > math.MaxUint16 {
		return nil, errors.New("Port number out of range")
	}

	command.Port = uint16(portNum)

	return command, nil
}

func handleCommand(w http.ResponseWriter, r *http.Request) {

	cmd, err := parseCommand(r.FormValue("text"), globalConfiguration)

	if err != nil {
		log.Println(err.Error())
		w.WriteHeader(400)
		w.Write([]byte(err.Error()))
		return
	}

	// TODO consolidate all these steps in package mcquery
	rw, err, kill := mcquery.Connect(cmd.Ip, cmd.Port)
	if err != nil {
		log.Println(err.Error())
		w.WriteHeader(400)
		w.Write([]byte("An error occurred while connecting to the MC server."))
		kill <- true
		return
	}
	challenge, err := mcquery.Handshake(rw)
	if err != nil {
		log.Println(err.Error())
		w.WriteHeader(400)
		w.Write([]byte("An error occurred while handshaking with MC server."))
		kill <- true
		return
	}
	statResponse, err := mcquery.BasicStat(rw, challenge)
	if err != nil {
		log.Println(err.Error())
		w.WriteHeader(400)
		w.Write([]byte("An error occurred while reading status of MC server."))
		kill <- true
		return
	}
	kill <- true

	responseMap := make(map[string]interface{})
	responseString := fmt.Sprintf("```MOTD: %s\n", statResponse.Motd)
	responseString += fmt.Sprintf("Gametype: %s\n", statResponse.Gametype)
	responseString += fmt.Sprintf("Map: %s\n", statResponse.Map)
	responseString += fmt.Sprintf("NumPlayers: %s\n", statResponse.NumPlayers)
	responseString += fmt.Sprintf("MaxPlayers: %s\n", statResponse.MaxPlayers)
	responseString += fmt.Sprintf("HostPort: %d\n", statResponse.HostPort)
	responseString += fmt.Sprintf("HostIp: %s\n```", statResponse.HostIp)
	responseMap["text"] = responseString
	if cmd.Hidden {
		responseMap["response_type"] = "ephemeral"
	} else {
		responseMap["response_type"] = "in_channel"
	}
	data, err := json.Marshal(responseMap)

	if err != nil {
		log.Println(err.Error())
		w.WriteHeader(400)
		w.Write([]byte("Internal JSON Error Ocurred."))
		return
	}

	w.Header().Set("content-type", "application/json")
	w.WriteHeader(200)
	w.Write(data)

}

func main() {

	log.Println("Starting mcbot server")

	port := flag.String("port", "80", "Port to bind to")
	configFile := flag.String("config", "", "Configuration file")
	flag.Parse()

	log.Println("Loading configuration")
	globalConfiguration = getConfiguration(*configFile)
	log.Printf("Configuration is %+v\n", globalConfiguration)

	http.HandleFunc("/", handleCommand)

	log.Printf("Binding to port %s\n", *port)

	err := http.ListenAndServe(fmt.Sprintf(":%s", *port), nil)

	if err != nil {
		panic(err)
	}

}
<|endoftext|>