generated from devries/aoc_template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
solution.go
137 lines (110 loc) · 3.39 KB
/
solution.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package day05p2
import (
"io"
"regexp"
"sort"
"strconv"
"strings"
"aoc/utils"
)
func Solve(r io.Reader) any {
lines := utils.ReadLines(r)
// Get seeds from first line
parts := strings.Fields(lines[0])
values := make([]int64, len(parts)-1)
for i := 0; i < len(values); i++ {
v, err := strconv.ParseInt(parts[i+1], 10, 64)
utils.Check(err, "Unable to convert %s to int64", parts[i+1])
values[i] = v
}
// For remaining lines create conversions and convert values
conversions := []Conversion{}
// three positive integers
re := regexp.MustCompile(`\d+\s+\d+\s+\d+`)
for _, ln := range lines[2:] {
switch {
case re.MatchString(ln):
// Build up the conversion array
parts = strings.Fields(ln)
components := make([]int64, len(parts))
for i, s := range parts {
var err error
components[i], err = strconv.ParseInt(s, 10, 64)
utils.Check(err, "Unable to convert %s to int64", s)
}
c := Conversion{Start: components[1], End: components[1] + components[2], Delta: components[0] - components[1]}
conversions = append(conversions, c)
case ln == "":
values = doConversion(conversions, values)
conversions = []Conversion{}
}
}
if len(conversions) > 0 {
values = doConversion(conversions, values)
}
min := values[0]
for i := 2; i < len(values); i += 2 {
if values[i] < min {
min = values[i]
}
}
return min
}
type Conversion struct {
Start int64
End int64
Delta int64
}
// Get the delta value by performing a binary search over sorted array of conversions
// also get the range of subsequent numbers for which it is valid
func getDeltaInterval(arr []Conversion, val int64) (int64, int64) {
low, high := 0, len(arr)-1
var mid int
for low <= high {
mid = low + (high-low)/2
if arr[mid].Start <= val && arr[mid].End > val {
return arr[mid].Delta, arr[mid].End - val
}
if arr[mid].Start > val {
high = mid - 1
} else if arr[mid].End <= val {
low = mid + 1
}
}
if arr[mid].Start > val {
return 0, arr[mid].Start - val
} else if mid < len(arr)-1 {
return 0, arr[mid+1].Start - val
} else {
return 0, 0
}
}
// Run conversions and return new values array
func doConversion(conversions []Conversion, values []int64) []int64 {
// Conversion array complete, calculate conversions
sort.Slice(conversions, func(i, j int) bool { return conversions[i].Start < conversions[j].Start })
newvalues := []int64{}
for i := 0; i < len(values); i += 2 {
// For each input value and range we convert to a new value and range.
// If the range is longer than the valid interval of the conversion we split up the range into two intervals
// and then convert the second value and range as well... if that one is longer than the valid interval we repeat
start, length := values[i], values[i+1]
for {
delta, interval := getDeltaInterval(conversions, start)
newvalues = append(newvalues, start+delta)
if length <= interval || interval == 0 { // 0 interval means the rest of the numbers follow that delta
// The length of the input value range is less than the conversion interval
newvalues = append(newvalues, length)
break
} else {
// The length of the input value range is greater than the remaining
// conversion interval, we need to split the solution up into multiple
// ranges
newvalues = append(newvalues, interval)
start = start + interval
length = length - interval
}
}
}
return newvalues
}