Files
adventofcode/2024/go/utils/utils.go
2024-12-01 08:02:28 +00:00

223 lines
3.9 KiB
Go

package utils
import (
"bufio"
"fmt"
"io"
"os"
"reflect"
"regexp"
"strconv"
"strings"
"golang.org/x/exp/constraints"
)
func PanicOnErr(err error) {
if err != nil {
panic(err)
}
}
const MaxInt = int(^uint(0) >> 1)
const MinInt = ^MaxInt
func Max[T constraints.Ordered](a, b T) T {
if a > b {
return a
}
return b
}
func Min[T constraints.Ordered](a, b T) T {
if a < b {
return a
}
return b
}
func SliceMinMax[T constraints.Ordered](slice []T) (*T, *T) {
if len(slice) == 0 {
return nil, nil
}
min := &slice[0]
max := &slice[0]
for i, v := range slice {
if v < *min {
min = &slice[i]
}
if v > *max {
max = &slice[i]
}
}
return min, max
}
func MustAtoi(s string) int {
v, err := strconv.Atoi(s)
PanicOnErr(err)
return v
}
// Returns key from map[T]int which has the max value
func MapFindMax(m interface{}) interface{} {
var maxK interface{} = nil
var maxV = MinInt
iter := reflect.ValueOf(m).MapRange()
for iter.Next() {
k := iter.Key()
v := int(iter.Value().Int())
if v > maxV {
maxV = v
maxK = k.Interface()
}
}
return maxK
}
// Returns key from map[T]int which has the min value
func MapFindMin(m interface{}) interface{} {
var minK interface{} = nil
var minV = MaxInt
iter := reflect.ValueOf(m).MapRange()
for iter.Next() {
k := iter.Key()
v := int(iter.Value().Int())
if v < minV {
minV = v
minK = k.Interface()
}
}
return minK
}
func Readfile(day int) string {
filename := fmt.Sprintf("day%02d/input.txt", day)
file, err := os.Open(filename)
PanicOnErr(err)
defer file.Close()
reader := bufio.NewReader(file)
contents, err := io.ReadAll(reader)
PanicOnErr(err)
return strings.TrimSuffix(string(contents), "\n")
}
func ReadfileAsSlice(day int) []string {
filename := fmt.Sprintf("day%02d/input.txt", day)
file, err := os.Open(filename)
PanicOnErr(err)
defer file.Close()
arr := make([]string, 0)
sc := bufio.NewScanner(file)
for sc.Scan() {
arr = append(arr, sc.Text())
}
return arr
}
func ParseToStruct(re *regexp.Regexp, input string, target interface{}) bool {
m := re.FindStringSubmatch(input)
if m == nil {
return false
}
var useOffset bool
for i, name := range re.SubexpNames() {
if i == 0 {
continue
}
var field reflect.Value
if name == "" {
// use offset
if i == 1 {
useOffset = true
} else if !useOffset {
panic("can't mix named and unnamed subexpressions")
}
field = reflect.ValueOf(target).Elem().Field(i - 1)
} else {
// use name
if i == 1 {
useOffset = false
} else if useOffset {
panic("can't mix named and unnamed subexpressions")
}
field = reflect.ValueOf(target).Elem().FieldByName(name)
}
if field.Kind() == reflect.String {
field.SetString(m[i])
} else if field.Kind() == reflect.Int {
v, err := strconv.Atoi(m[i])
PanicOnErr(err)
field.SetInt(int64(v))
} else if field.Kind() == reflect.Uint8 {
if len(m[i]) != 1 {
panic(fmt.Sprintf("expecting 1 char, got: %s", m[i]))
}
field.SetUint(uint64(m[i][0]))
} else {
panic(fmt.Sprintf("unknown kind: %s", field.Kind()))
}
}
return true
}
func MustParseToStruct(re *regexp.Regexp, input string, target interface{}) {
if !ParseToStruct(re, input, target) {
panic(fmt.Errorf("failed to parse: %s", input))
}
}
func CharToLower(c byte) byte {
return strings.ToLower(string(c))[0]
}
func CharToUpper(c byte) byte {
return strings.ToUpper(string(c))[0]
}
func Contains(haystack []string, needle string) bool {
for _, s := range haystack {
if s == needle {
return true
}
}
return false
}
func Abs[T constraints.Signed](x T) T {
if x < 0 {
return -x
}
return x
}
func Gcd(x, y int) int {
if x <= 0 || y <= 0 {
panic(fmt.Errorf("invalid input: %d, %d", x, y))
}
if x == y {
return x
}
if x > y {
return Gcd(x-y, y)
} else {
return Gcd(x, y-x)
}
}
func Sign[T constraints.Signed](x T) int {
if x < 0 {
return -1
} else if x > 0 {
return 1
} else {
return 0
}
}