import std; alias Unit = void[0]; alias Set(T) = Unit[T]; auto toSet(R)(R r) { return r.map!(element => tuple(element, Unit.init)).assocArray; } auto setOf(T)(T[] elements...) { return elements.toSet; } void main() { auto nodes = File("input", "r").byLine.map!(to!int).chain([0]).toSet; nodes.calculateNumberOfPathsLeadingFromTo(0, nodes.byKey.maxElement).writeln; } ulong calculateNumberOfPathsLeadingFromTo(Set!int nodes, int start, int end) { if (end == start) { return 1; } return iota(end - 3, end).filter!(potentialAncestor => potentialAncestor in nodes) .map!(ancestor => nodes.memoize!calculateNumberOfPathsLeadingFromTo(start, ancestor)) .sum; } unittest { Set!int input = setOf(0, 1); assert(input.calculateNumberOfPathsLeadingFromTo(0, 1) == 1); } unittest { auto input = [16, 10, 15, 5, 1, 11, 7, 19, 6, 12, 4].chain([0]).toSet; assert(input.calculateNumberOfPathsLeadingFromTo(0, input.byKey.maxElement) == 8); } unittest { auto input = [ 28, 33, 18, 42, 31, 14, 46, 20, 48, 47, 24, 23, 49, 45, 19, 38, 39, 11, 1, 32, 25, 35, 8, 17, 7, 9, 4, 2, 34, 10, 3 ].chain([0]).toSet; assert(input.calculateNumberOfPathsLeadingFromTo(0, input.byKey.maxElement) == 19_208); }