package main

import (
	"errors"
	"fmt"
	"math"
	"sort"
)

// Latencies is a deterministic single-server FIFO model, in arbitrary time units.
// open arrivals occur at 0, gap, 2*gap...; closed arrivals wait for completion.
func Latencies(service []int, gap int, open bool) ([]int, error) {
	if gap < 0 {
		return nil, errors.New("negative gap")
	}
	end := 0
	out := make([]int, 0, len(service))
	for i, cost := range service {
		if cost < 0 {
			return nil, errors.New("negative service")
		}
		arrival := end
		if open {
			arrival = i * gap
		}
		start := max(end, arrival)
		end = start + cost
		out = append(out, end-arrival)
	}
	return out, nil
}
func Mean(xs []int) float64 {
	var total int
	for _, v := range xs {
		total += v
	}
	if len(xs) == 0 {
		return math.NaN()
	}
	return float64(total) / float64(len(xs))
}
func Percentile(xs []int, p float64) (int, error) {
	if len(xs) == 0 || p <= 0 || p > 1 || math.IsNaN(p) {
		return 0, errors.New("invalid percentile")
	}
	copy := append([]int(nil), xs...)
	sort.Ints(copy)
	return copy[int(math.Ceil(p*float64(len(copy))))-1], nil
}
func Replicas(peak, safeRate float64, lost int) (int, error) {
	if peak <= 0 || safeRate <= 0 || lost < 0 || math.IsNaN(peak) || math.IsNaN(safeRate) || math.IsInf(peak, 0) || math.IsInf(safeRate, 0) {
		return 0, errors.New("invalid capacity")
	}
	return int(math.Ceil(peak/safeRate)) + lost, nil
}
func main() {
	service := []int{100, 1, 1, 1, 1, 1, 1, 1, 1, 1}
	open, _ := Latencies(service, 10, true)
	closed, _ := Latencies(service, 10, false)
	op, _ := Percentile(open, .5)
	cp, _ := Percentile(closed, .5)
	fmt.Printf("open: %v mean=%.1f p50=%d\n", open, Mean(open), op)
	fmt.Printf("closed: %v mean=%.1f p50=%d\n", closed, Mean(closed), cp)
	fmt.Printf("Little's Law example: L=%.1f\n", 1000*.010)
	n, err := Replicas(2400, 600, 1)
	fmt.Println("replicas for one-instance loss:", n, err)
}
