223 lines
3.9 KiB
Go
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
|
|
}
|
|
}
|