diff --git a/13input.txt b/13input.txt
new file mode 100644
index 0000000..0a1f478
--- /dev/null
+++ b/13input.txt
@@ -0,0 +1,2 @@
+1000299
+41,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,37,x,x,x,x,x,971,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,17,13,x,x,x,x,23,x,x,x,x,x,29,x,487,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,19
\ No newline at end of file
diff --git a/2020.csproj b/2020.csproj
index 953c3e8..99f0fef 100644
--- a/2020.csproj
+++ b/2020.csproj
@@ -47,6 +47,9 @@
PreserveNewest
+
+ PreserveNewest
+
diff --git a/Program.cs b/Program.cs
index dc64140..6394332 100644
--- a/Program.cs
+++ b/Program.cs
@@ -17,6 +17,7 @@
Q10.Go();
Q11.Go();
Q12.Go();
+ Q13.Go();
Util.Log($"Total time={(System.DateTime.Now - start).TotalMilliseconds}ms");
}
}
diff --git a/Q13.cs b/Q13.cs
new file mode 100644
index 0000000..402ece0
--- /dev/null
+++ b/Q13.cs
@@ -0,0 +1,171 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace _2020
+{
+ class Q13
+ {
+ static int leaveTime;
+ static List busses = new List();
+
+ public static void Go()
+ {
+ var start = DateTime.Now;
+ MakeList();
+ Util.Log($"Q13 MakeList took {(DateTime.Now - start).TotalMilliseconds}ms");
+ var p1start = DateTime.Now;
+ Part1();
+ Util.Log($"Q13 part1 took {(DateTime.Now - p1start).TotalMilliseconds}ms");
+ var p2start = DateTime.Now;
+ Part2();
+ Util.Log($"Q13 part2 took {(DateTime.Now - p2start).TotalMilliseconds}ms");
+
+ Util.Log($"Q13 took {(DateTime.Now - start).TotalMilliseconds}ms");
+ }
+
+ static void MakeList()
+ {
+ var lines = File.ReadAllLines("13input.txt");
+ if (lines.Length != 2)
+ {
+ throw new Exception("Invalid input");
+ }
+
+ leaveTime = Convert.ToInt32(lines[0]);
+ foreach (var bus in lines[1].Split(','))
+ {
+ if (bus == "x")
+ {
+ busses.Add(-1);
+ continue;
+ }
+
+ busses.Add(Convert.ToInt32(bus));
+ }
+ }
+
+ static void Part1()
+ {
+ int smallestModulo = int.MaxValue;
+ int smallestBus = -1;
+ foreach (var bus in busses)
+ {
+ if (bus < 0)
+ {
+ continue;
+ }
+
+ var mod = bus - (leaveTime % bus);
+ if (mod < smallestModulo)
+ {
+ smallestModulo = mod;
+ smallestBus = bus;
+ }
+ }
+
+ Util.Log($"Q13Part1: soonest bus={smallestBus}, departs={leaveTime + smallestModulo}, wait time={smallestModulo}m, answer={smallestBus * smallestModulo}");
+ }
+
+ // taken from https://rosettacode.org/wiki/Chinese_remainder_theorem#C.23, modified for int64s. i don't know how this works. who am i, sun-tzu?
+ static long GetChineseRemainderTheorem(IEnumerable n, IEnumerable a)
+ {
+ long prod = n.Aggregate(1L, (i, j) => i * j);
+ long p;
+ long sm = 0;
+ for (int i = 0; i < n.Count(); i++)
+ {
+ p = prod / n.ElementAt(i);
+ sm += a.ElementAt(i) * ModularMultiplicativeInverse(p, n.ElementAt(i)) * p;
+ }
+ return sm % prod;
+ }
+
+ static long ModularMultiplicativeInverse(long a, long mod)
+ {
+ long b = a % mod;
+ for (long x = 1; x < mod; x++)
+ {
+ if ((b * x) % mod == 1)
+ {
+ return x;
+ }
+ }
+ return 1;
+ }
+
+ static void Part2()
+ {
+ var a = new List();
+ var n = new List();
+ for (int i = 0; i < busses.Count; i++)
+ {
+ if (busses[i] <= 0)
+ {
+ continue;
+ }
+
+ a.Add(busses[i] - i % busses[i]);
+ n.Add(busses[i]);
+ }
+
+ Util.Log($"Q13Part2: val={GetChineseRemainderTheorem(n, a)}");
+ }
+
+ // this is how i initially solved it. took ~30mins. i'm not proud of it, but the "real" answer apparently required specialized knowledge i did not have.
+ static void Part2BruteForce()
+ {
+ int root = busses.First(x => x > 0);
+ var sorted = busses.OrderBy(x => x);
+ var largest = sorted.Last();
+
+ long checkVal = 0;
+
+ var idx = busses.IndexOf(largest);
+ int answersFound = 0;
+ int numThreads = 13;
+ Parallel.For(0, numThreads, thread =>
+ {
+ var threadVal = checkVal + (thread * largest);
+ long rootCheckVal = 0;
+ while (answersFound == 0)
+ {
+ threadVal += numThreads * largest;
+ rootCheckVal = threadVal - idx;
+ if (rootCheckVal % root == 0)
+ {
+ var gotit = false;
+ for (int i = 1; i < busses.Count; i++)
+ {
+ if (busses[i] <= 0)
+ {
+ continue;
+ }
+
+ if ((rootCheckVal + i) % busses[i] == 0)
+ {
+ gotit = true;
+ }
+ else
+ {
+ gotit = false;
+ break;
+ }
+ }
+
+ if (gotit)
+ {
+ Interlocked.Increment(ref answersFound);
+ break;
+ }
+ }
+ }
+
+ Util.Log($"Q13Part2: val={rootCheckVal}");
+ });
+ }
+ }
+}