diff --git a/2025/one.go b/2025/one/one.go similarity index 100% rename from 2025/one.go rename to 2025/one/one.go diff --git a/2025/two/two.go b/2025/two/two.go new file mode 100644 index 0000000..2aabf8e --- /dev/null +++ b/2025/two/two.go @@ -0,0 +1,138 @@ +package main + +import ( + "bufio" + "bytes" + "fmt" + "log" + "os" + "strconv" + "strings" +) + +func scanComma(data []byte, atEOF bool) (advance int, token []byte, err error) { + if atEOF && len(data) == 0 { + return 0, nil, nil + } + if i := bytes.IndexByte(data, ','); i > 0 { + return i + 1, data[0:i], nil + } + if atEOF { + if len(data) > 0 && data[len(data)-1] == '\n' { + return len(data), data[0 : len(data)-1], nil + } + return len(data), data, nil + } + return 0, nil, nil + +} + +func check(err error) { + if err != nil { + log.Fatal(err) + } +} + +func intPow(base int, exponent int) int { + + if exponent == 0 { + return 1 + } else if exponent == 1 { + return base + } + + val := base + remaining_mults := exponent - 1 + for ; remaining_mults > 0; remaining_mults-- { + val = val * base + } + return val +} + +func makeCandidateInt(prefix int, n_repeats int) (int, error) { + prefixStr := strconv.Itoa(prefix) + + var candidateBuilder strings.Builder + + for ; n_repeats > 0; n_repeats-- { + candidateBuilder.WriteString(prefixStr) + } + + return strconv.Atoi(candidateBuilder.String()) + +} + +func largestPrefix(rangeEnd string) (int, error) { + if len(rangeEnd)%2 == 0 { + return strconv.Atoi(rangeEnd[0 : len(rangeEnd)/2]) + } else { + prefixLen := (len(rangeEnd) - 1) / 2 + return intPow(10, prefixLen) - 1, nil + } +} + +func sumInt(seq []int) int { + sum := 0 + + for _, val := range seq { + sum += val + } + return sum +} + +func getInvalidsForRange(start int, end int) []int { + maxPrefix, err := largestPrefix(strconv.Itoa(end)) + check(err) + + invalidIds := make(map[int]struct{}) + + fmt.Printf("Considering range %d - %d, max prefix %d\n", start, end, maxPrefix) + for candidatePrefix := 1; candidatePrefix <= maxPrefix; candidatePrefix++ { + for n_repeats := 2; ; n_repeats++ { + candidateInt, err := makeCandidateInt(candidatePrefix, n_repeats) + check(err) + + if candidateInt > end { + break + } + + if (start <= candidateInt) && (candidateInt <= end) { + fmt.Printf("Can use %d\n", candidateInt) + invalidIds[candidateInt] = struct{}{} + } + } + } + + invalidIdList := make([]int, 0, len(invalidIds)) + + for invalidId := range invalidIds { + invalidIdList = append(invalidIdList, invalidId) + + } + return invalidIdList +} + +func main() { + file, err := os.Open("two.txt") + check(err) + defer file.Close() + + invalidIdSum := 0 + + scanner := bufio.NewScanner(file) + scanner.Split(scanComma) + + for scanner.Scan() { + idRange := scanner.Text() + + idRangeSplit := strings.SplitN(idRange, "-", 2) + + rangeStart, err := strconv.Atoi(idRangeSplit[0]) + check(err) + rangeEnd, err := strconv.Atoi(idRangeSplit[1]) + check(err) + + invalidIdSum += sumInt(getInvalidsForRange(rangeStart, rangeEnd)) + } + fmt.Printf("%d\n", invalidIdSum) +} diff --git a/2025/two/two_test.go b/2025/two/two_test.go new file mode 100644 index 0000000..3c16ce7 --- /dev/null +++ b/2025/two/two_test.go @@ -0,0 +1,72 @@ +package main + +import "testing" + +type invalidTest struct { + arg1, arg2 int + expected []int +} + +var invalidTests = []invalidTest{ + {11, 22, []int{11, 22}}, + {95, 115, []int{111, 99}}, + {998, 1012, []int{999, 1010}}, + {1188511880, 1188511890, []int{1188511885}}, + {222220, 222224, []int{222222}}, + {1698522, 1698528, []int{}}, + {446443, 446449, []int{446446}}, + {38593856, 38593862, []int{38593859}}, + {565653, 565659, []int{565656}}, + {824824821, 824824827, []int{824824824}}, + {2121212118, 2121212124, []int{2121212121}}, +} + +func TestGetInvalids(t *testing.T) { + for _, test := range invalidTests { + out := getInvalidsForRange(test.arg1, test.arg2) + + if len(out) != len(test.expected) { + t.Errorf("Output %q not equal to expected %q", out, test.expected) + } + + for i, got := range out { + want := test.expected[i] + + if got != want { + t.Errorf("Expected %d to be %d at position %d in %q", got, want, i, out) + } + } + } +} + +func TestIntPow(t *testing.T) { + val := intPow(10, 1) + if val != 10 { + t.Errorf("Was expecting 10, not %d", val) + } + + val = intPow(10, 2) + if val != 100 { + t.Errorf("Was expecting 100, not %d", val) + } + + val = intPow(10, 3) + if val != 1000 { + t.Errorf("Was expecting 1000, not %d", val) + } + +} + +func TestLargestPrefix(t *testing.T) { + prefix, _ := largestPrefix("4444") + + if prefix != 44 { + t.Errorf("Should have return prefix 44, not %d", prefix) + } + + prefix, _ = largestPrefix("44444") + + if prefix != 99 { + t.Errorf("Should have returned prefix 99, not %d", prefix) + } +}