Let's revisit the problem description here.
We are implementing an extrapolation algorithm that reminds me to my university years when we did Newton interpolation, or was it Lagrange? I don't remember anymore, but it was using a similar algorithm to this.
Part 1 and Part 2 differs only in the direction of the extrapolation, and one can be implemented using the other, just like I did it below.
namespace AdventOfCode.Y2023.Day09;
using System;
using System.Linq;
[ProblemName("Mirage Maintenance")]
class Solution : Solver {
public object PartOne(string input) => Solve(input, ExtrapolateRight);
public object PartTwo(string input) => Solve(input, ExtrapolateLeft);
long Solve(string input, Func<long[], long> extrapolate) =>
input.Split("\n").Select(ParseNumbers).Select(extrapolate).Sum();
long[] ParseNumbers(string line) =>
line.Split(" ").Select(long.Parse).ToArray();
// It's a common trick to zip a sequence with the skipped version of itself
long[] Diff(long[] numbers) =>
numbers.Zip(numbers.Skip(1)).Select(p => p.Second - p.First).ToArray();
// I went a bit further and recurse until there are no numbers left. It's
// more compact this way and doesn't affect the runtime much.
long ExtrapolateRight(long[] numbers) =>
!numbers.Any() ? 0 : ExtrapolateRight(Diff(numbers)) + numbers.Last();
long ExtrapolateLeft(long[] numbers) =>
ExtrapolateRight(numbers.Reverse().ToArray());
}
Please ☆ my repo if you like it!