package main

import (
	"bytes"
	"fmt"
	"io"
	"math"
	"strconv"
)

type UserID int64
type IDs []UserID
type IntOrInt64 interface{ ~int | ~int64 }

func Sum[T IntOrInt64](xs ...T) T {
	var out T
	for _, x := range xs {
		out += x
	}
	return out
}
func Count[T comparable](xs []T) map[T]int {
	out := make(map[T]int)
	for _, x := range xs {
		out[x]++
	}
	return out
}
func Filter[S ~[]E, E any](src S, keep func(E) bool) S {
	var out S
	for _, x := range src {
		if keep(x) {
			out = append(out, x)
		}
	}
	return out
}
func FilterPlain[E any](src []E, keep func(E) bool) []E { return Filter(src, keep) }
func Zero[T any]() T                                    { var z T; return z }
func Identity[T any](x T) T                             { return x }

type IntFunc func(int) int

type Box[T any] struct{ Value T }

func (b Box[T]) Get() T                        { return b.Value }
func (b Box[T]) Map[U any](f func(T) U) Box[U] { return Box[U]{Value: f(b.Value)} }
func emit(w io.Writer, s string) error         { _, err := io.WriteString(w, s); return err }
func dynamicKeyPanics() (panicked bool) {
	defer func() { panicked = recover() != nil }()
	_ = Count([]any{[]int{1}})
	return false
}
func main() {
	fmt.Println("counts:", Count([]string{"go", "rust", "go"}))
	fmt.Printf("sum: %T %v\n", Sum(UserID(2), UserID(3)), Sum(UserID(2), UserID(3)))
	ids := IDs{1, 2, 3, 4}
	keep := func(id UserID) bool { return id%2 == 0 }
	got, plain := Filter(ids, keep), FilterPlain(ids, keep)
	fmt.Printf("filter: %T %v\n", got, got)
	fmt.Printf("plain: %T %v\n", plain, plain)
	fmt.Println("zero:", Zero[int](), Zero[string]() == "")
	f := Identity[int]
	var g func(int) int = Identity
	h := IntFunc(Identity)
	fmt.Println("function inference:", f(7), g(8), h(9))
	b := Box[int]{Value: 42}
	converted := b.Map(strconv.Itoa)
	fmt.Printf("generic method: %T %q\n", converted, converted.Get())
	fmt.Println("mixed keys:", len(Count([]any{1, "one", 1})))
	fmt.Println("slice key panics:", dynamicKeyPanics())
	nan := math.NaN()
	fmt.Println("NaN:", nan == nan, len(Count([]float64{nan, nan})))
	var out bytes.Buffer
	if err := emit(&out, "hello"); err != nil {
		panic(err)
	}
	fmt.Println("writer:", out.String())
}
