Day 20
This commit is contained in:
305
src/Days/D20.c
Normal file
305
src/Days/D20.c
Normal file
@@ -0,0 +1,305 @@
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define max(a,b) \
|
||||
({ \
|
||||
__typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a > _b ? _a : _b; \
|
||||
})
|
||||
|
||||
///////////////////
|
||||
// AVL-TREE NODE //
|
||||
///////////////////
|
||||
|
||||
typedef struct Node {
|
||||
struct Node *parent;
|
||||
struct Node *leftChild;
|
||||
struct Node *rightChild;
|
||||
int32_t size;
|
||||
int32_t height;
|
||||
int64_t value;
|
||||
} Node;
|
||||
|
||||
////////////////////////////////
|
||||
// NULL-SAFE NODE DATA ACCESS //
|
||||
////////////////////////////////
|
||||
|
||||
int32_t height(Node *node)
|
||||
{
|
||||
return node == NULL ? 0 : node->height;
|
||||
}
|
||||
|
||||
int32_t size(Node *node)
|
||||
{
|
||||
return node == NULL ? 0 : node->size;
|
||||
}
|
||||
|
||||
int balance(Node *node)
|
||||
{
|
||||
return node == NULL ? 0 : height(node->leftChild) - height(node->rightChild);
|
||||
}
|
||||
|
||||
|
||||
///////////
|
||||
// RESET //
|
||||
///////////
|
||||
|
||||
void resetHeight(Node *node)
|
||||
{
|
||||
node->height = 1 + max(height(node->leftChild), height(node->rightChild));
|
||||
}
|
||||
|
||||
void resetSize(Node *node)
|
||||
{
|
||||
node->size = 1 + size(node->leftChild) + size(node->rightChild);
|
||||
}
|
||||
|
||||
|
||||
///////////////////
|
||||
// AVL-ROTATIONS //
|
||||
///////////////////
|
||||
|
||||
Node *rotateL(Node *root)
|
||||
{
|
||||
assert(root);
|
||||
assert(root->rightChild);
|
||||
|
||||
Node *newRoot = root->rightChild;
|
||||
Node *transfer = newRoot->leftChild;
|
||||
|
||||
newRoot->leftChild = root;
|
||||
root->rightChild = transfer;
|
||||
|
||||
newRoot->parent = root->parent;
|
||||
root->parent = newRoot;
|
||||
|
||||
if (transfer != NULL) transfer->parent = root;
|
||||
|
||||
resetSize(root);
|
||||
resetSize(newRoot);
|
||||
|
||||
resetHeight(root);
|
||||
resetHeight(newRoot);
|
||||
|
||||
return newRoot;
|
||||
}
|
||||
|
||||
Node *rotateR(Node *root)
|
||||
{
|
||||
assert(root);
|
||||
assert(root->leftChild);
|
||||
|
||||
Node *newRoot = root->leftChild;
|
||||
Node *transfer = newRoot->rightChild;
|
||||
|
||||
newRoot->rightChild = root;
|
||||
root->leftChild = transfer;
|
||||
|
||||
newRoot->parent = root->parent;
|
||||
root->parent = newRoot;
|
||||
|
||||
if (transfer != NULL) transfer->parent = root;
|
||||
|
||||
resetSize(root);
|
||||
resetSize(newRoot);
|
||||
|
||||
resetHeight(root);
|
||||
resetHeight(newRoot);
|
||||
|
||||
return newRoot;
|
||||
}
|
||||
|
||||
///////////////////
|
||||
// MODIFICATIONS //
|
||||
///////////////////
|
||||
|
||||
Node *insert_pos_rec(Node *root, int pos, Node *newNode, Node *parent)
|
||||
{
|
||||
assert(0 <= pos && (!root || pos <= root->size));
|
||||
|
||||
if (root == NULL) {
|
||||
assert(pos == 0);
|
||||
|
||||
newNode->leftChild = NULL;
|
||||
newNode->rightChild = NULL;
|
||||
if (parent != NULL) newNode->parent = parent;
|
||||
|
||||
resetSize(newNode);
|
||||
resetHeight(newNode);
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
if (pos <= size(root->leftChild))
|
||||
root->leftChild = insert_pos_rec(root->leftChild, pos, newNode, root);
|
||||
else
|
||||
root->rightChild = insert_pos_rec(root->rightChild, pos - size(root->leftChild) - 1,
|
||||
newNode, root);
|
||||
|
||||
resetSize(root);
|
||||
resetHeight(root);
|
||||
|
||||
int bal = balance(root);
|
||||
|
||||
if (bal > 1) {
|
||||
if (pos > size(root->leftChild->leftChild))
|
||||
root->leftChild = rotateL(root->leftChild);
|
||||
|
||||
root = rotateR(root);
|
||||
}
|
||||
|
||||
if (bal < -1) {
|
||||
int posUpd = pos - size(root->leftChild) - 1;
|
||||
|
||||
if (posUpd <= size(root->rightChild->leftChild))
|
||||
root->rightChild = rotateR(root->rightChild);
|
||||
|
||||
root = rotateL(root);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
Node *insert_pos(Node *root, int pos, Node *newNode)
|
||||
{
|
||||
return insert_pos_rec(root, pos, newNode, NULL);
|
||||
}
|
||||
|
||||
Node *delete_pos_rec(Node *root, int pos, Node **victim)
|
||||
{
|
||||
assert(root);
|
||||
assert(0 <= pos && (!root || pos <= root->size));
|
||||
|
||||
if (pos < size(root->leftChild))
|
||||
root->leftChild = delete_pos_rec(root->leftChild, pos, victim);
|
||||
else if (pos > size(root->leftChild))
|
||||
root->rightChild = delete_pos_rec(root->rightChild, pos - size(root->leftChild) - 1, victim);
|
||||
else {
|
||||
*victim = root;
|
||||
|
||||
if (root->leftChild == NULL) return root->rightChild;
|
||||
if (root->rightChild == NULL) return root->leftChild;
|
||||
|
||||
Node *replacement;
|
||||
root->rightChild = delete_pos_rec(root->rightChild, 0, &replacement);
|
||||
|
||||
replacement->leftChild = root->leftChild;
|
||||
replacement->rightChild = root->rightChild;
|
||||
replacement->parent = root->parent;
|
||||
|
||||
root = replacement;
|
||||
}
|
||||
|
||||
if (root->leftChild) root->leftChild->parent = root;
|
||||
if (root->rightChild) root->rightChild->parent = root;
|
||||
|
||||
resetSize(root);
|
||||
resetHeight(root);
|
||||
|
||||
int bal = balance(root);
|
||||
|
||||
if (bal > 1) {
|
||||
if (balance(root->leftChild) < 0)
|
||||
root->leftChild = rotateL(root->leftChild);
|
||||
|
||||
root = rotateR(root);
|
||||
}
|
||||
|
||||
if (bal < -1) {
|
||||
if (balance(root->rightChild) > 0)
|
||||
root->rightChild = rotateR(root->rightChild);
|
||||
|
||||
root = rotateL(root);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
Node *delete_pos(Node *root, int pos, Node **victim)
|
||||
{
|
||||
return delete_pos_rec(root, pos, victim);
|
||||
}
|
||||
|
||||
int wrap(long long n, int mod)
|
||||
{
|
||||
return (n % mod + mod) % mod;
|
||||
}
|
||||
|
||||
//////////////
|
||||
// QUERYING //
|
||||
//////////////
|
||||
|
||||
Node *node_at_position(Node *root, int pos)
|
||||
{
|
||||
assert(root);
|
||||
assert(0 <= pos && pos <= root->size);
|
||||
|
||||
if (pos < size(root->leftChild))
|
||||
return node_at_position(root->leftChild, pos);
|
||||
else if (pos > size(root->leftChild))
|
||||
return node_at_position(root->rightChild, pos - size(root->leftChild) - 1);
|
||||
else
|
||||
return root;
|
||||
}
|
||||
|
||||
int32_t position_of_node(Node *node) {
|
||||
int32_t pos = size(node->leftChild);
|
||||
|
||||
for (; node->parent; node = node->parent)
|
||||
if (node->parent->rightChild == node)
|
||||
pos += size(node->parent->leftChild) + 1;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
/////////////
|
||||
// FFI API //
|
||||
/////////////
|
||||
|
||||
typedef struct CyclicList {
|
||||
Node *root;
|
||||
Node *nodes;
|
||||
} CyclicList;
|
||||
|
||||
int32_t position_of_nth_value(CyclicList *cl, int32_t n) {
|
||||
assert(0 <= n && n < cl->root->size);
|
||||
return position_of_node(&cl->nodes[n]);
|
||||
}
|
||||
|
||||
int64_t value_at_position(CyclicList *cl, int32_t pos) {
|
||||
return node_at_position(cl->root, wrap(pos, cl->root->size))->value;
|
||||
}
|
||||
|
||||
CyclicList *cyclic_list_from_array(int64_t *values, int numValues) {
|
||||
CyclicList* cl = malloc(sizeof(CyclicList));
|
||||
|
||||
cl->root = NULL;
|
||||
cl->nodes = (Node *) malloc(sizeof(Node) * numValues);
|
||||
|
||||
for (int i = 0; i < numValues; i++)
|
||||
cl->nodes[i].value = values[i];
|
||||
|
||||
for (int i = 0; i < numValues; i++)
|
||||
cl->root = insert_pos(cl->root, i, &cl->nodes[i]);
|
||||
|
||||
return cl;
|
||||
}
|
||||
|
||||
void mix(CyclicList *cl, int32_t rounds) {
|
||||
Node *victim;
|
||||
|
||||
int length = cl->root->size;
|
||||
for (int j = 0; j < rounds; j++) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
int idx = position_of_node(&cl->nodes[i]);
|
||||
cl->root = delete_pos(cl->root, idx, &victim);
|
||||
int newIdx = wrap(idx + victim->value, length - 1);
|
||||
cl->root = insert_pos(cl->root, newIdx, victim);
|
||||
}
|
||||
}
|
||||
}
|
||||
69
src/Days/D20.hs
Normal file
69
src/Days/D20.hs
Normal file
@@ -0,0 +1,69 @@
|
||||
{-# LANGUAGE ImportQualifiedPost #-}
|
||||
|
||||
module Days.D20 where
|
||||
|
||||
import Common
|
||||
import Data.Int (Int32, Int64)
|
||||
import Data.Maybe (fromJust)
|
||||
import Data.Vector.Storable qualified as Vector
|
||||
import Foreign qualified
|
||||
import Parse
|
||||
import System.IO.Unsafe (unsafePerformIO)
|
||||
|
||||
-- Dummy types for FFI
|
||||
data CyclicList = CyclicList
|
||||
|
||||
foreign import ccall unsafe "cyclic_list_from_array"
|
||||
cMkCyclicList ::
|
||||
Foreign.Ptr Int64 ->
|
||||
Int32 ->
|
||||
Foreign.Ptr CyclicList
|
||||
|
||||
foreign import ccall unsafe "mix"
|
||||
cMix ::
|
||||
Foreign.Ptr CyclicList ->
|
||||
Int32 ->
|
||||
IO ()
|
||||
|
||||
foreign import ccall unsafe "position_of_nth_value"
|
||||
cPositionOfNthValue ::
|
||||
Foreign.Ptr CyclicList ->
|
||||
Int32 ->
|
||||
Int32
|
||||
|
||||
foreign import ccall unsafe "value_at_position"
|
||||
cValueAtPosition ::
|
||||
Foreign.Ptr CyclicList ->
|
||||
Int32 ->
|
||||
Int64
|
||||
|
||||
parser :: Parser [Int]
|
||||
parser = someLines int
|
||||
|
||||
decrypt :: Int -> [Int] -> [Int64]
|
||||
decrypt rounds numbers =
|
||||
let iv = Vector.fromList $ map (fromIntegral :: Int -> Int64) numbers
|
||||
zeroIndex = fromJust $ Vector.elemIndex 0 iv
|
||||
(dataPtr, dataLen) = Vector.unsafeToForeignPtr0 iv
|
||||
groveOffsets = [1000, 2000, 3000]
|
||||
in unsafePerformIO $
|
||||
Foreign.withForeignPtr
|
||||
dataPtr
|
||||
( \ptr -> do
|
||||
let cycList = cMkCyclicList ptr (fromIntegral dataLen)
|
||||
cMix cycList (fromIntegral rounds)
|
||||
let zeroPos = cPositionOfNthValue cycList (fromIntegral zeroIndex)
|
||||
return $ map (cValueAtPosition cycList . (+ zeroPos)) groveOffsets
|
||||
)
|
||||
|
||||
part1 :: [Int] -> Int64
|
||||
part1 = sum . decrypt 1
|
||||
|
||||
decryptionKey :: Int
|
||||
decryptionKey = 811589153
|
||||
|
||||
part2 :: [Int] -> Int64
|
||||
part2 = sum . decrypt 10 . map (* decryptionKey)
|
||||
|
||||
day :: Day
|
||||
day = parsecDay parser (definitive . part1, definitive . part2)
|
||||
@@ -31,6 +31,7 @@ import Days.D16 qualified as D16
|
||||
import Days.D17 qualified as D17
|
||||
import Days.D18 qualified as D18
|
||||
import Days.D19 qualified as D19
|
||||
import Days.D20 qualified as D20
|
||||
|
||||
import Data.ByteString.Char8 qualified as BS
|
||||
import Data.Text.IO qualified as T
|
||||
@@ -62,4 +63,5 @@ days =
|
||||
, [D17.day]
|
||||
, [D18.day]
|
||||
, [D19.day]
|
||||
, [D20.day]
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user