#include #include #include #include #include #include #include #include #include constexpr char IGNORE_CHAR = 'X'; constexpr auto MASK_PATTERN = R"(mask = ([X0-9]+))"; constexpr auto MEM_PATTERN = R"(mem\[(\d+)\] = (\d+))"; /** * Represents a block of instructions with a given mask */ class InstructionBlock { public: InstructionBlock(const std::string &mask, const std::vector> &storeInstructions) : mask(mask), storeInstructions(storeInstructions) { } InstructionBlock(std::string &&mask, std::vector> &&storeInstructions) : mask(std::move(mask)), storeInstructions(std::move(storeInstructions)) { } /** * Mask a value to be stored in memory, in accordance with the mask for this block * @param num The number to mask * @return long long The masked value */ long long maskValue(long long num) const { // We are dealing with a 36 bit numebr so we must use a long long to guarantee we can fit it long long res = num; for (std::string::size_type i = 0; i < this->mask.size(); i++) { char maskChar = this->mask.at(this->mask.size() - i - 1); if (maskChar == IGNORE_CHAR) { continue; } else { res = this->setBitAt(res, i, maskChar); } } return res; } /** * Mask a memory address, in accordance with the mask for this block * @param address The address to mask * @return std::vector All of the possible masked addresses */ std::vector maskMemoryAddress(long long address) const { return this->recursivelyMaskAddresses(address); } const std::vector> &getStoreInstructions() const { return this->storeInstructions; } private: std::string mask; std::vector> storeInstructions; /** * Set a bit at the given position * @param value The value set the value within * @param position The position to set the bit at * @param bit The bit to set, either 0 or 1 * @return long long The value with the bit set */ long long setBitAt(long long value, int position, char bit) const { auto res = value; if (bit == '0') { res &= ~(1LL << position); } else if (bit == '1') { res |= (1LL << position); } else { throw std::invalid_argument("bit must be zero or one"); } return res; } /** * Perform the masking needed for the memory address * @param address The address to mask * @param startIdx The index to start the search at * @return std::vector All of the possible masked addresses */ std::vector recursivelyMaskAddresses(long long address, std::string::size_type startIdx = 0) const { std::vector results; long long res = address; for (std::string::size_type i = startIdx; i < this->mask.size(); i++) { char maskChar = this->mask.at(this->mask.size() - i - 1); if (maskChar == IGNORE_CHAR) { auto masked0 = this->setBitAt(res, i, '0'); auto masked1 = this->setBitAt(res, i, '1'); auto results0 = this->recursivelyMaskAddresses(masked0, i + 1); auto results1 = this->recursivelyMaskAddresses(masked1, i + 1); results.insert(results.end(), results0.begin(), results0.end()); results.insert(results.end(), results1.begin(), results1.end()); } else if (maskChar == '1') { res = this->setBitAt(res, i, maskChar); } } results.push_back(res); return results; } }; std::vector readInput(const std::string &filename) { std::vector input; std::string line; std::ifstream file(filename); while (std::getline(file, line)) { input.push_back(line); } return input; } /** * Parse the input into InstructionBlocks * @param input The input to parse * @return std::vector The parsed input */ std::vector parseInput(const std::vector &input) { std::vector blocks; std::regex maskExpression(MASK_PATTERN); std::regex memExpression(MEM_PATTERN); std::string currentMask; std::vector> currentStoreInstructions; for (auto it = input.cbegin(); it != input.cend(); it++) { auto line = *it; std::smatch matches; if (std::regex_match(line, matches, maskExpression)) { // We don't want to emplace on the first mask we find if (it != input.cbegin()) { blocks.emplace_back(std::move(currentMask), std::move(currentStoreInstructions)); currentStoreInstructions.clear(); } currentMask = matches[1]; } else if (std::regex_match(line, matches, memExpression)) { int address = std::stoi(matches[1]); int value = std::stoi(matches[2]); currentStoreInstructions.emplace_back(address, value); } } // Put in the last one we've found blocks.emplace_back(std::move(currentMask), std::move(currentStoreInstructions)); return blocks; } long long part1(const std::vector &instructions) { std::unordered_map memory; for (const InstructionBlock &instruction : instructions) { for (const std::pair &storeInstruction : instruction.getStoreInstructions()) { auto maskedValue = instruction.maskValue(storeInstruction.second); memory[storeInstruction.first] = maskedValue; } } return std::accumulate( memory.cbegin(), memory.cend(), 0LL, [](long long total, std::pair memoryItem) { return total + memoryItem.second; }); } long long part2(const std::vector &instructions) { std::unordered_map memory; for (const InstructionBlock &instruction : instructions) { for (const std::pair &storeInstruction : instruction.getStoreInstructions()) { for (const auto maskedAddress : instruction.maskMemoryAddress(storeInstruction.first)) { memory[maskedAddress] = storeInstruction.second; } } } return std::accumulate( memory.cbegin(), memory.cend(), 0LL, [](long long total, std::pair memoryItem) { return total + memoryItem.second; }); } int main(int argc, char *argv[]) { if (argc != 2) { std::cerr << argv[0] << " " << std::endl; return 1; } auto input = readInput(argv[1]); auto parsedInput = parseInput(input); std::cout << part1(parsedInput) << std::endl; std::cout << part2(parsedInput) << std::endl; }