using AdvenOfCode.Contracts; namespace AoC_2025; public class Day02 : IPuzzleSolver { private string[] ParsePuzzleInput(string path) { var puzzleInput = File.ReadAllText(path) .Trim() .Split(',', StringSplitOptions.RemoveEmptyEntries); return puzzleInput; } public long SolvePart1(string pathToPuzzleInput) { var idRanges = ParsePuzzleInput(pathToPuzzleInput); var invalidIds = idRanges.SelectMany(range => FindInvalidIds(range)); var sum = invalidIds.Sum(); return sum; } public long SolvePart2(string pathToPuzzleInput) { var idRanges = ParsePuzzleInput(pathToPuzzleInput); var invalidIds = idRanges.SelectMany(range => FindInvalidSegmentedIds(range)); var sum = invalidIds.Sum(); return sum; } private IEnumerable FindInvalidIds(string range) { var (start, end) = Parse(range); var halvableIds = FindHalvableIds(start, end); var invalidIds = GetInvalidIds(halvableIds); return invalidIds; } private IEnumerable FindInvalidSegmentedIds(string range) { var (start, end) = Parse(range); var idsAsAllSegments = GetIdAsAllSegments(start, end); var invalidIdSegments = FindIdsWhereAllSegmentsAreEqual(idsAsAllSegments); var invalidIds = MergeSegments(invalidIdSegments); var invalidIdNumbers = invalidIds.Select(id => long.Parse(id)); return invalidIdNumbers; } private (long start, long end) Parse(string range) { var parts = range.Split('-', StringSplitOptions.RemoveEmptyEntries); return (long.Parse(parts[0]), long.Parse(parts[1])); } private IEnumerable FindHalvableIds(long start, long end) { var ids = Range(start, end); var idStrings = ids.Select(id => id.ToString()); var halvableIds = idStrings.Where(id => (id.Length % 2) == 0); return halvableIds; } private IEnumerable GetInvalidIds(IEnumerable halvableIds) { var idParts = halvableIds.Select(id => (id[..(id.Length / 2)], id[(id.Length / 2)..])); var invalidIds = idParts.Where(parts=> parts.Item1 == parts.Item2); var invalidNumberedIds = invalidIds.Select(id => long.Parse(id.Item1 + id.Item2)); return invalidNumberedIds; } private IEnumerable Range(long start, long end) { for (var i = start; i <= end; i++) { yield return i; } } private IEnumerable>> GetIdAsAllSegments(long start, long end) { var ids = Range(start, end); var idStrings = ids.Select(id => id.ToString()); var idSegments = idStrings.Select(id => GetSegments(id)); return idSegments; } private IEnumerable> GetSegments(string id) { for (int i = id.Length / 2; i > 0; i--) { if (id.Length % i == 0) { yield return id.Chunk(i).Select(chunks => new string(chunks)); } } } private IEnumerable> FindIdsWhereAllSegmentsAreEqual(IEnumerable>> idAllSegments) { var segmentsWithOneAllMatching = FilterForAnyAllSegmentsEqual(idAllSegments); var idSegments = GetFirstForEachSegments(segmentsWithOneAllMatching); return idSegments; } private static IEnumerable> GetFirstForEachSegments(IEnumerable>> segmentsWithOneAllMatching) { var idSegments = segmentsWithOneAllMatching .Select(segments => segments.First()); return idSegments; } private IEnumerable MergeSegments(IEnumerable> segments) { return segments.Select(segment => string.Concat(segment)); } private IEnumerable>> FilterForAnyAllSegmentsEqual(IEnumerable>> idAllSegments) { var segmentsWithOneAllMatching = idAllSegments .Where(idSegments => idSegments.Any(segments => CheckAllSegmentsEqual(segments))); return segmentsWithOneAllMatching; } private bool CheckAllSegmentsEqual(IEnumerable segments) { var (allEqual, _) = segments.Aggregate( (allEqual: true, prev: ""), (tuple, segment) => (tuple.allEqual && (tuple.prev == "" || tuple.prev == segment), segment)); return allEqual; } }