using System.Collections.Generic;
namespace AdventOfCode2020
[TestFixture(TestName = "Day 14: Docking Data")]
public static class Day14
public static void Main()
var result = Part2(TestInput);
Console.WriteLine("Result: " + result);
private sealed record MemCell(string Address, long Value);
private static string ApplyMask(long addr, in string mask)
var addrStr = new char[36];
for (var i = 0; i < 36; i++)
addrStr[idx] = (addr & 1) == 1 ? '1' : '0';
addrStr[idx] = mask[idx];
private static bool HasUniqueFloatingBits(in ReadOnlySpan<char> cellAddr, in ReadOnlySpan<char> valAddr)
var hasUniqueFloatingBits = false;
for (var idx = 0; idx < 36; idx++)
if (cellAddr[idx] != valAddr[idx] && valAddr[idx] != 'X')
hasUniqueFloatingBits = true;
return hasUniqueFloatingBits;
private static bool DoIntersect(in ReadOnlySpan<char> addr1, in ReadOnlySpan<char> addr2)
for (var i = 0; i < 36; i++)
if (addr1[i] == '0' && addr2[i] == '1'
|| addr1[i] == '1' && addr2[i] == '0')
private static List<MemCell> Apply(this List<MemCell> list, in MemCell val)
var result = new HashSet<MemCell>(list.Count);
foreach (var cell in list)
if (cell.Address == val.Address)
else if (DoIntersect(cell.Address, val.Address))
var cellAddr = cell.Address.ToCharArray();
for (var i = 0; i < 36; i++)
if (cellAddr[i] != 'X' || val.Address[i] == 'X')
var newAddr = (char[])cellAddr.Clone();
if (val.Address[i] == '0')
if (HasUniqueFloatingBits(newAddr, val.Address))
result.Add(new(new(newAddr), cell.Value));
if (untouched && HasUniqueFloatingBits(cell.Address, val.Address))
var newResult = new List<MemCell>(result.OrderBy(v => v.Address));
Console.WriteLine("".PadLeft(36, '-'));
foreach (var cell in newResult)
Console.WriteLine($"{cell.Address}: {cell.Value}");
private static List<MemCell> ParseProgram2(in string input)
var result = new List<MemCell>();
var mask = "".PadLeft(36, '0');
foreach (var line in input.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries))
var parts = line.Split(" = ", StringSplitOptions.TrimEntries);
var addr = long.Parse(parts[0][4..^1]);
var val = long.Parse(parts[1]);
var memCell = new MemCell(ApplyMask(addr, mask), val);
Console.WriteLine($"{Environment.NewLine}{memCell.Address} <- {memCell.Value}");
result = result.Apply(memCell);
[TestCase(TestInput, ExpectedResult = (1L << 34)*101)]
[TestCase(TestInput2, ExpectedResult = 208)]
[TestCase(TestInput3, ExpectedResult = 1*3+2*4)]
[TestCase(TestInput4, ExpectedResult = 1*2+2*4)]
[TestCase(TestInput5, ExpectedResult = 1+2*2)]
[TestCase(TestInput6, ExpectedResult = 1*2+2*2)]
[TestCase(TestInput7, ExpectedResult = 1*2+2*2)]
public static long Part2(in string input)
var mem = ParseProgram2(input);
foreach (var cell in mem)
var floatingBits = cell.Address.Count(c => c == 'X');
result += (1L << floatingBits) * cell.Value;
private const string TestInput = @"
mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X
private const string TestInput2 = @"
mask = 000000000000000000000000000000X1001X
mask = 00000000000000000000000000000000X0XX
private const string TestInput3 = @"
mask = 0000000000000000000000000000000000XX
mask = 0000000000000000000000000000000XX000
private const string TestInput4 = @"
mask = 00000000000000000000000000000000001X
mask = 0000000000000000000000000000000XX000
private const string TestInput5 = @"
mask = 00000000000000000000000000000000001X
mask = 0000000000000000000000000000000000X0
private const string TestInput6 = @"
mask = 0000000000000000000000000000000000XX
mask = 0000000000000000000000000000000000X0
private const string TestInput7 = @"
mask = 00000000000000000000000000000000X001
mask = 0000000000000000000000000000000001X0