diff --git a/advent-of-code-2022.csproj b/advent-of-code-2022.csproj
index 729f342..619169b 100644
--- a/advent-of-code-2022.csproj
+++ b/advent-of-code-2022.csproj
@@ -2,12 +2,13 @@
Exe
- net6.0
+ net7.0
aoc2022
enable
enable
False
True
+ default
diff --git a/src/01.cs b/src/01.cs
index 71efa47..0d9f470 100644
--- a/src/01.cs
+++ b/src/01.cs
@@ -7,7 +7,7 @@ internal class Day01 : Day
internal override void Parse()
{
List calories = new();
- foreach (var line in Util.ReadAllLines("01"))
+ foreach (var line in Util.Parsing.ReadAllLines("01"))
{
if (string.IsNullOrEmpty(line))
{
diff --git a/src/02.cs b/src/02.cs
index 3d0e193..8e5c161 100644
--- a/src/02.cs
+++ b/src/02.cs
@@ -6,7 +6,7 @@ internal class Day02 : Day
internal override void Parse()
{
- lines = Util.ReadAllLines("02");
+ lines = Util.Parsing.ReadAllLines("02");
}
internal override string Part1()
diff --git a/src/03.cs b/src/03.cs
index d10fc79..a5a754c 100644
--- a/src/03.cs
+++ b/src/03.cs
@@ -6,7 +6,7 @@ internal class Day03 : Day
internal override void Parse()
{
- sacks = Util.ReadAllLines("03");
+ sacks = Util.Parsing.ReadAllLines("03");
}
static int GetPriority(char x) => x <= 'Z' ? x - 'A' + 27 : x - 'a' + 1;
diff --git a/src/04.cs b/src/04.cs
index 8c1f8ef..e0058a1 100644
--- a/src/04.cs
+++ b/src/04.cs
@@ -6,7 +6,7 @@ internal class Day04 : Day
internal override void Parse()
{
- foreach (var line in Util.ReadAllLines("04"))
+ foreach (var line in Util.Parsing.ReadAllLines("04"))
{
var assignments = line.Split(',');
var firstAssignment = assignments[0].Split('-');
diff --git a/src/05.cs b/src/05.cs
index d5b4dd1..7440bda 100644
--- a/src/05.cs
+++ b/src/05.cs
@@ -17,7 +17,7 @@ internal class Day05 : Day
internal override void Parse()
{
int state = 0;
- foreach (var line in Util.ReadAllLines("05"))
+ foreach (var line in Util.Parsing.ReadAllLines("05"))
{
if (state == 0)
{
diff --git a/src/06.cs b/src/06.cs
index 98d82ac..8bc5364 100644
--- a/src/06.cs
+++ b/src/06.cs
@@ -6,7 +6,7 @@ internal class Day06 : Day
internal override void Parse()
{
- buffer = Util.ReadAllText("06");
+ buffer = Util.Parsing.ReadAllText("06");
}
private static int FindDistinct(string buf, int distinctLen)
diff --git a/src/07.cs b/src/07.cs
index ca8de15..91b317f 100644
--- a/src/07.cs
+++ b/src/07.cs
@@ -2,7 +2,7 @@
internal class Day07 : Day
{
- private class file
+ private class fileInfo
{
public long size;
// ReSharper disable once NotAccessedField.Local
@@ -11,11 +11,11 @@ internal class Day07 : Day
public override string ToString() => $"{name}, {size:N0}b";
}
- private class dir
+ private class dirInfo
{
- public dir? outer;
- public readonly List dirs = new();
- public readonly List files = new();
+ public dirInfo? outer;
+ public readonly List dirs = new();
+ public readonly List files = new();
public string name = string.Empty;
public long size => files.Sum(x => x.size) + dirs.Sum(x => x.size);
@@ -23,13 +23,13 @@ internal class Day07 : Day
public override string ToString() => $"{name}, {size:N0}b, {dirs.Count} dir{(dirs.Count == 1 ? "" : "s")}, {files.Count} file{(files.Count == 1 ? "" : "s")}{(outer != null ? $", parent '{outer.name}'" : "")}";
}
- private readonly dir rootDir = new() {name = "/"};
+ private readonly dirInfo rootDirInfo = new() {name = "/"};
internal override void Parse()
{
- dir? curr = null;
+ dirInfo? curr = null;
- foreach (var line in Util.ReadAllLines("07"))
+ foreach (var line in Util.Parsing.ReadAllLines("07"))
{
if (line.StartsWith("$"))
{
@@ -45,7 +45,7 @@ internal class Day07 : Day
{
if (arg == "/")
{
- curr = rootDir;
+ curr = rootDirInfo;
}
else if (arg == "..")
{
@@ -62,17 +62,17 @@ internal class Day07 : Day
var parts = line.Split(' ');
if (parts[0] == "dir")
{
- curr!.dirs.Add(new dir() { name = parts[1], outer = curr });
+ curr!.dirs.Add(new dirInfo() { name = parts[1], outer = curr });
}
else
{
- curr!.files.Add(new file { size = long.Parse(parts[0]), name = parts[1] });
+ curr!.files.Add(new fileInfo { size = long.Parse(parts[0]), name = parts[1] });
}
}
}
}
- private static IEnumerable GetCandidates(dir root, long? threshold = null)
+ private static IEnumerable GetCandidates(dirInfo root, long? threshold = null)
{
if (threshold == null || root.size <= threshold)
{
@@ -95,15 +95,15 @@ internal class Day07 : Day
internal override string Part1()
{
- List candidates = new(GetCandidates(rootDir, 100000));
+ List candidates = new(GetCandidates(rootDirInfo, 100000));
return $"Sum of directories below 100,000 bytes: <+white>{candidates.Sum(x => x.size)}";
}
internal override string Part2()
{
- List flatDirList = new(GetCandidates(rootDir));
- var rootSize = rootDir.size;
+ List flatDirList = new(GetCandidates(rootDirInfo));
+ var rootSize = rootDirInfo.size;
const int totalSize = 70000000;
var currentFreeSpace = totalSize - rootSize;
const int totalNeededFreeSpace = 30000000;
diff --git a/src/08.cs b/src/08.cs
index 0b7b6e1..8c8cfdf 100644
--- a/src/08.cs
+++ b/src/08.cs
@@ -6,7 +6,7 @@ internal class Day08 : Day
internal override void Parse()
{
- var lines = new List(Util.ReadAllLines("08"));
+ var lines = new List(Util.Parsing.ReadAllLines("08"));
trees = new int[lines.Count][];
for (int i = 0; i < lines.Count; i++)
{
diff --git a/src/Template.cs b/src/Template.cs
index ec22ecf..08c42ab 100644
--- a/src/Template.cs
+++ b/src/Template.cs
@@ -3,11 +3,9 @@ namespace aoc2022;
internal class DayTemplate : Day
{
- IEnumerable? lines;
-
internal override void Parse()
{
- lines = Util.ReadAllLines("##");
+ var lines = Util.Parsing.ReadAllLines("##");
}
internal override string Part1()
diff --git a/src/Util.cs b/src/Util.cs
index e78618e..5f28270 100644
--- a/src/Util.cs
+++ b/src/Util.cs
@@ -1,118 +1 @@
-using System.Diagnostics;
-using System.Reflection;
-using System.Text;
-
-namespace aoc2022;
-
-internal static class Util
-{
- private static readonly char[] StripPreamble = { (char)8745, (char)9559, (char)9488, };
- private static readonly Encoding[] StripBOMsFromEncodings = { Encoding.UTF8, Encoding.Unicode, Encoding.BigEndianUnicode, };
- private static void ReadData(string inputName, Action processor)
- {
- if (Console.IsInputRedirected)
- {
- bool processedSomething = false;
- for (int i = 0; Console.In.ReadLine() is { } line; i++)
- {
- if (i == 0)
- {
- if (line[0..StripPreamble.Length].SequenceEqual(StripPreamble))
- {
- line = line[StripPreamble.Length..];
- }
- else
- {
- foreach (var encoding in StripBOMsFromEncodings)
- {
- if (line.StartsWith(encoding.GetString(encoding.GetPreamble()), StringComparison.Ordinal))
- {
- line = line.Replace(encoding.GetString(encoding.GetPreamble()), "", StringComparison.Ordinal);
- }
- }
- }
- }
- processor(line);
- if (!string.IsNullOrEmpty(line))
- {
- processedSomething = true;
- }
- }
-
- if (processedSomething)
- {
- return;
- }
- }
-
- var filename = $"inputs/{inputName}.txt";
- if (File.Exists(filename))
- {
- foreach (var line in File.ReadLines(filename))
- {
- processor(line);
- }
-
- return;
- }
-
- // typeof(Util) is not technically correct since what we need is the "default namespace,"
- // but "default namespace" is a Project File concept, not a C#/.NET concept, so it's not
- // accessible at runtime. instead, we assume Util is also part of the "default namespace"
- var resourceName = $"{typeof(Util).Namespace}.inputs.{inputName}.txt";
- using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
- using StreamReader reader = new(stream!);
- while (reader.ReadLine() is { } readLine)
- {
- processor(readLine);
- }
- }
-
- internal static string ReadAllText(string filename)
- {
- string contents = string.Empty;
- ReadData(filename, (line) => contents = line);
- return contents;
- }
-
- internal static IEnumerable ReadAllLines(string filename)
- {
- List lines = new();
- ReadData(filename, (line) => lines.Add(line));
- return lines;
- }
-
- internal static IEnumerable ReadAllLinesAsInts(string filename)
- {
- return ReadAllLines(filename).Select(long.Parse);
- }
-
- internal static void StartTestSet(string name)
- {
- Logger.Log($"test: {name}");
- }
-
- internal static void StartTest(string label)
- {
- Logger.Log($"{label}");
- }
-
- internal static void TestCondition(Func a, bool printResult = true)
- {
- if (a.Invoke() == false)
- {
- Debug.Assert(false);
- if (printResult)
- {
- Logger.Log("x");
- }
- }
- else
- {
- if (printResult)
- {
- Logger.Log("✓");
- }
- }
- }
-}
+
\ No newline at end of file
diff --git a/src/Util/Bisect.cs b/src/Util/Bisect.cs
new file mode 100644
index 0000000..0041ace
--- /dev/null
+++ b/src/Util/Bisect.cs
@@ -0,0 +1,51 @@
+using System.Numerics;
+
+namespace aoc2022.Util;
+
+public static class Bisect
+{
+ // Bisect takes a known-good low and known-bad high value as the bounds
+ // to bisect, and a function to test each value for success or failure.
+ // If the function succeeds, the value is adjusted toward the maximum,
+ // and if the function fails, the value is adjusted toward the minimum.
+ // The final value is returned when the difference between the success
+ // and the failure is less than or equal to the acceptance threshold
+ // (usually 1, for integers).
+ public static double Find(double low, double high, double threshold, Func tryFunc)
+ {
+ while (System.Math.Abs(high - low) > threshold)
+ {
+ var currVal = low + ((high - low) / 2);
+ var success = tryFunc(currVal);
+ if (success)
+ {
+ low = currVal;
+ }
+ else
+ {
+ high = currVal;
+ }
+ }
+
+ return low;
+ }
+
+ public static double Find(long low, long high, long threshold, Func tryFunc)
+ {
+ while (System.Math.Abs(high - low) > threshold)
+ {
+ var currVal = low + ((high - low) / 2);
+ var success = tryFunc(currVal);
+ if (success)
+ {
+ low = currVal;
+ }
+ else
+ {
+ high = currVal;
+ }
+ }
+
+ return low;
+ }
+}
\ No newline at end of file
diff --git a/src/Util/Combinatorics.cs b/src/Util/Combinatorics.cs
new file mode 100644
index 0000000..d668cd7
--- /dev/null
+++ b/src/Util/Combinatorics.cs
@@ -0,0 +1,38 @@
+namespace aoc2022.Util;
+
+public static class Combinatorics
+{
+ public static IEnumerable> GetPermutations(IList list)
+ {
+ Action, int>? helper = null;
+ List> res = new();
+
+ helper = (arr, n) =>
+ {
+ if (n == 1)
+ {
+ var tmp = new T[arr.Count];
+ arr.CopyTo(tmp, 0);
+ res.Add(tmp);
+ }
+ else
+ {
+ for (var i = 0; i < n; i++)
+ {
+ helper!(arr, n - 1);
+ if (n % 2 == 1)
+ {
+ (arr[i], arr[n - 1]) = (arr[n - 1], arr[i]);
+ }
+ else
+ {
+ (arr[0], arr[n - 1]) = (arr[n - 1], arr[0]);
+ }
+ }
+ }
+ };
+
+ helper(list, list.Count);
+ return res;
+ }
+}
diff --git a/src/Util/Math.cs b/src/Util/Math.cs
new file mode 100644
index 0000000..17d5804
--- /dev/null
+++ b/src/Util/Math.cs
@@ -0,0 +1,46 @@
+using System.Numerics;
+
+namespace aoc2022.Util;
+
+public static class Math
+{
+ public static ulong GCD(ulong a, ulong b)
+ {
+ while (true)
+ {
+ if (b == 0)
+ {
+ return a;
+ }
+
+ var a1 = a;
+ a = b;
+ b = a1 % b;
+ }
+ }
+
+ public static ulong LCM(params ulong[] nums)
+ {
+ var num = nums.Length;
+ switch (num)
+ {
+ case 0:
+ return 0;
+ case 1:
+ return nums[0];
+ }
+
+ var ret = lcm(nums[0], nums[1]);
+ for (var i = 2; i < num; i++)
+ {
+ ret = lcm(nums[i], ret);
+ }
+
+ return ret;
+ }
+
+ private static ulong lcm(ulong a, ulong b)
+ {
+ return (a * b) / GCD(a, b);
+ }
+}
\ No newline at end of file
diff --git a/src/Util/Parsing.cs b/src/Util/Parsing.cs
new file mode 100644
index 0000000..e213a6e
--- /dev/null
+++ b/src/Util/Parsing.cs
@@ -0,0 +1,88 @@
+using System.Reflection;
+using System.Text;
+
+namespace aoc2022.Util;
+
+public static class Parsing
+{
+ private static readonly char[] StripPreamble = { (char)8745, (char)9559, (char)9488, };
+ private static readonly Encoding[] StripBOMsFromEncodings = { Encoding.UTF8, Encoding.Unicode, Encoding.BigEndianUnicode, };
+ private static void ReadData(string inputName, Action processor)
+ {
+ if (Console.IsInputRedirected)
+ {
+ bool processedSomething = false;
+ for (int i = 0; Console.In.ReadLine() is { } line; i++)
+ {
+ if (i == 0)
+ {
+ if (line[0..StripPreamble.Length].SequenceEqual(StripPreamble))
+ {
+ line = line[StripPreamble.Length..];
+ }
+ else
+ {
+ foreach (var encoding in StripBOMsFromEncodings)
+ {
+ if (line.StartsWith(encoding.GetString(encoding.GetPreamble()), StringComparison.Ordinal))
+ {
+ line = line.Replace(encoding.GetString(encoding.GetPreamble()), "", StringComparison.Ordinal);
+ }
+ }
+ }
+ }
+ processor(line);
+ if (!string.IsNullOrEmpty(line))
+ {
+ processedSomething = true;
+ }
+ }
+
+ if (processedSomething)
+ {
+ return;
+ }
+ }
+
+ var filename = $"inputs/{inputName}.txt";
+ if (File.Exists(filename))
+ {
+ foreach (var line in File.ReadLines(filename))
+ {
+ processor(line);
+ }
+
+ return;
+ }
+
+ // typeof(Logger) is not technically correct since what we need is the "default namespace,"
+ // but "default namespace" is a Project File concept, not a C#/.NET concept, so it's not
+ // accessible at runtime. instead, we assume Logger is also part of the "default namespace"
+ var resourceName = $"{typeof(Logger).Namespace}.inputs.{inputName}.txt";
+ using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
+ using StreamReader reader = new(stream!);
+ while (reader.ReadLine() is { } readLine)
+ {
+ processor(readLine);
+ }
+ }
+
+ internal static string ReadAllText(string filename)
+ {
+ string contents = string.Empty;
+ ReadData(filename, (line) => contents = line);
+ return contents;
+ }
+
+ internal static IEnumerable ReadAllLines(string filename)
+ {
+ List lines = new();
+ ReadData(filename, (line) => lines.Add(line));
+ return lines;
+ }
+
+ internal static IEnumerable ReadAllLinesAsInts(string filename)
+ {
+ return ReadAllLines(filename).Select(long.Parse);
+ }
+}
\ No newline at end of file
diff --git a/src/Util/Testing.cs b/src/Util/Testing.cs
new file mode 100644
index 0000000..8bd2c83
--- /dev/null
+++ b/src/Util/Testing.cs
@@ -0,0 +1,35 @@
+using System.Diagnostics;
+
+namespace aoc2022.Util;
+
+public static class Testing
+{
+ internal static void StartTestSet(string name)
+ {
+ Logger.Log($"test: {name}");
+ }
+
+ internal static void StartTest(string label)
+ {
+ Logger.Log($"{label}");
+ }
+
+ internal static void TestCondition(Func a, bool printResult = true)
+ {
+ if (a.Invoke() == false)
+ {
+ Debug.Assert(false);
+ if (printResult)
+ {
+ Logger.Log("x");
+ }
+ }
+ else
+ {
+ if (printResult)
+ {
+ Logger.Log("✓");
+ }
+ }
+ }
+}
\ No newline at end of file