Compare commits

...

9 Commits

Author SHA1 Message Date
Bananymous 840cd80994 AOC2023: Implement day14 2023-12-19 02:49:48 +02:00
Bananymous 432a9847a8 AOC2023: Implement day13 2023-12-19 01:25:23 +02:00
Bananymous a8300d7a82 Kernel: Implement hacky non-block read for ps2 keyboard 2023-12-19 00:20:46 +02:00
Bananymous ed54f128bc Kernel: Allow opening files with O_NONBLOCK 2023-12-19 00:20:15 +02:00
Bananymous 0c208fa526 LibC: Fix stpncpy
I had misunderstood and tought that the string is always
null terminated
2023-12-15 00:33:37 +02:00
Bananymous c0ce2fd9a7 LibC: Optimize malloc even further
aoc2023/day12 now runs in 3.5 seconds on my machine. This is way
better than the old almost hour.
2023-12-14 23:49:25 +02:00
Bananymous 57eea81c7b LibC: Compile with -O2 optimizations
I have no idea why libc had no optimizations enabled.

Weird thing is that memcpy optimized to infinite loop if I kept the
__restrict__ attributes in pointers. I don't think there was any ub.
2023-12-14 23:40:08 +02:00
Bananymous 1800f62de4 LibC: Optimize malloc by a lot
I now cache first free node in malloc_pool and whether the node is
last or not. There allow doing less full iterations over the whole
malloc pool.

Malloc is still unbearably slow and I will have to write a proper
fast malloc at some point. With this patch running aoc2023/day12 is
atleast possible. Probabaly will take closer to an hour...
2023-12-14 15:31:00 +02:00
Bananymous 7787042eae LibC: Mark __assert_fail as noreturn 2023-12-14 15:13:54 +02:00
14 changed files with 1968 additions and 37 deletions

View File

@ -64,6 +64,7 @@ namespace Kernel::Input
protected:
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
virtual bool has_data_impl() const override;
private:
const dev_t m_rdev;

View File

@ -234,4 +234,10 @@ namespace Kernel::Input
}
}
bool PS2Keyboard::has_data_impl() const
{
CriticalScope _;
return !m_event_queue.empty();
}
}

View File

@ -55,7 +55,7 @@ namespace Kernel
BAN::ErrorOr<int> OpenFileDescriptorSet::open(BAN::StringView absolute_path, int flags)
{
if (flags & ~(O_RDONLY | O_WRONLY | O_NOFOLLOW | O_SEARCH | O_APPEND | O_TRUNC | O_CLOEXEC | O_TTY_INIT | O_DIRECTORY))
if (flags & ~(O_RDONLY | O_WRONLY | O_NOFOLLOW | O_SEARCH | O_APPEND | O_TRUNC | O_CLOEXEC | O_TTY_INIT | O_DIRECTORY | O_NONBLOCK))
return BAN::Error::from_errno(ENOTSUP);
int access_mask = O_EXEC | O_RDONLY | O_WRONLY | O_SEARCH;

View File

@ -47,7 +47,7 @@ add_custom_target(crtx-install
add_library(libc ${LIBC_SOURCES})
add_dependencies(libc headers crtx-install)
target_compile_options(libc PRIVATE -g -Wstack-usage=512)
target_compile_options(libc PRIVATE -O2 -g -Wstack-usage=512)
target_compile_options(libc PUBLIC -Wall -Wextra -Werror -Wno-error=stack-usage=)
add_custom_target(libc-install

View File

@ -13,7 +13,7 @@
__BEGIN_DECLS
void __assert_fail(const char*, const char*, int, const char*);
[[noreturn]] void __assert_fail(const char*, const char*, int, const char*);
__END_DECLS

View File

@ -17,13 +17,19 @@ static consteval size_t log_size_t(size_t value, size_t base)
static constexpr size_t s_malloc_pool_size_initial = 4096;
static constexpr size_t s_malloc_pool_size_multiplier = 2;
static constexpr size_t s_malloc_pool_count = sizeof(size_t) * 8 - log_size_t(s_malloc_pool_size_initial, s_malloc_pool_size_multiplier);
static constexpr size_t s_malloc_default_align = 16;
static constexpr size_t s_malloc_default_align = alignof(max_align_t);
// This is indirectly smallest allowed allocation
static constexpr size_t s_malloc_shrink_threshold = 64;
struct malloc_node_t
{
bool allocated;
// TODO: these two pointers could be put into data region
malloc_node_t* prev_free;
malloc_node_t* next_free;
size_t size;
uint8_t data[0];
bool allocated;
bool last;
alignas(s_malloc_default_align) uint8_t data[0];
size_t data_size() const { return size - sizeof(malloc_node_t); }
malloc_node_t* next() { return (malloc_node_t*)(data + data_size()); }
@ -33,6 +39,11 @@ struct malloc_pool_t
{
uint8_t* start;
size_t size;
malloc_node_t* free_list;
uint8_t* end() { return start + size; }
bool contains(malloc_node_t* node) { return start <= (uint8_t*)node && (uint8_t*)node < end(); }
};
static malloc_pool_t s_malloc_pools[s_malloc_pool_count];
@ -44,6 +55,7 @@ void init_malloc()
{
s_malloc_pools[i].start = nullptr;
s_malloc_pools[i].size = pool_size;
s_malloc_pools[i].free_list = nullptr;;
pool_size *= s_malloc_pool_size_multiplier;
}
}
@ -64,10 +76,32 @@ static bool allocate_pool(size_t pool_index)
auto* node = (malloc_node_t*)pool.start;
node->allocated = false;
node->size = pool.size;
node->last = true;
node->prev_free = nullptr;
node->next_free = nullptr;
pool.free_list = node;
return true;
}
static void remove_node_from_pool_free_list(malloc_pool_t& pool, malloc_node_t* node)
{
if (node == pool.free_list)
{
pool.free_list = pool.free_list->next_free;
if (pool.free_list)
pool.free_list->prev_free = nullptr;
}
else
{
if (node->next_free)
node->next_free->prev_free = node->prev_free;
if (node->prev_free)
node->prev_free->next_free = node->next_free;
}
}
static void* allocate_from_pool(size_t pool_index, size_t size)
{
assert(size % s_malloc_default_align == 0);
@ -75,27 +109,30 @@ static void* allocate_from_pool(size_t pool_index, size_t size)
auto& pool = s_malloc_pools[pool_index];
assert(pool.start != nullptr);
uint8_t* pool_end = pool.start + pool.size;
if (!pool.free_list)
return nullptr;
for (auto* node = (malloc_node_t*)pool.start; (uint8_t*)node < pool_end; node = node->next())
for (auto* node = pool.free_list; node; node = node->next_free)
{
if (node->allocated)
continue;
assert(!node->allocated);
// merge nodes right after current one
while (!node->last && !node->next()->allocated)
{
// merge two unallocated nodes next to each other
auto* next = node->next();
if ((uint8_t*)next < pool_end && !next->allocated)
node->size += next->size;
remove_node_from_pool_free_list(pool, next);
node->last = next->last;
node->size += next->size;
}
if (node->data_size() < size)
continue;
node->allocated = true;
remove_node_from_pool_free_list(pool, node);
// shrink node if needed
if (node->data_size() - size > sizeof(malloc_node_t))
if (node->data_size() - size >= sizeof(malloc_node_t) + s_malloc_shrink_threshold)
{
uint8_t* node_end = (uint8_t*)node->next();
@ -104,6 +141,16 @@ static void* allocate_from_pool(size_t pool_index, size_t size)
auto* next = node->next();
next->allocated = false;
next->size = node_end - (uint8_t*)next;
next->last = node->last;
node->last = false;
// insert excess node to free list
if (pool.free_list)
pool.free_list->prev_free = next;
next->next_free = pool.free_list;
next->prev_free = nullptr;
pool.free_list = next;
}
return node->data;
@ -117,6 +164,14 @@ static malloc_node_t* node_from_data_pointer(void* data_pointer)
return (malloc_node_t*)((uint8_t*)data_pointer - sizeof(malloc_node_t));
}
static malloc_pool_t& pool_from_node(malloc_node_t* node)
{
for (size_t i = 0; i < s_malloc_pool_count; i++)
if (s_malloc_pools[i].start && s_malloc_pools[i].contains(node))
return s_malloc_pools[i];
assert(false);
}
void* malloc(size_t size)
{
// align size to s_malloc_default_align boundary
@ -165,23 +220,7 @@ void* realloc(void* ptr, size_t size)
if (oldsize == size)
return ptr;
// shrink allocation if needed
if (oldsize > size)
{
if (node->data_size() - size > sizeof(malloc_node_t))
{
uint8_t* node_end = (uint8_t*)node->next();
node->size = sizeof(malloc_node_t) + size;
auto* next = node->next();
next->allocated = false;
next->size = node_end - (uint8_t*)next;
}
return ptr;
}
// FIXME: try to expand allocation
// TODO: try to shrink or expand allocation
// allocate new pointer
void* new_ptr = malloc(size);
@ -203,10 +242,25 @@ void free(void* ptr)
auto* node = node_from_data_pointer(ptr);
// mark node as unallocated and try to merge with the next node
node->allocated = false;
if (!node->next()->allocated)
node->size += node->next()->size;
auto& pool = pool_from_node(node);
// merge nodes right after freed one
while (!node->last && !node->next()->allocated)
{
auto* next = node->next();
remove_node_from_pool_free_list(pool, next);
node->last = next->last;
node->size += next->size;
}
// add node to free list
if (pool.free_list)
pool.free_list->prev_free = node;
node->prev_free = nullptr;
node->next_free = pool.free_list;
pool.free_list = node;
}
void* calloc(size_t nmemb, size_t size)

View File

@ -17,7 +17,7 @@ int memcmp(const void* s1, const void* s2, size_t n)
return 0;
}
void* memcpy(void* __restrict__ dstp, const void* __restrict__ srcp, size_t n)
void* memcpy(void* dstp, const void* srcp, size_t n)
{
unsigned char* dst = static_cast<unsigned char*>(dstp);
const unsigned char* src = static_cast<const unsigned char*>(srcp);
@ -89,7 +89,6 @@ char* stpncpy(char* __restrict__ dest, const char* __restrict__ src, size_t n)
dest[i] = src[i];
for (; n; i++, n--)
dest[i] = '\0';
dest[i] = '\0';
return &dest[i];
}

View File

@ -15,6 +15,8 @@ set(AOC2023_PROJECTS
day10
day11
day12
day13
day14
)
set(BANAN_AOC2023_BIN ${BANAN_BIN}/aoc2023)

View File

@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 3.26)
project(aoc2023_day13 CXX)
set(SOURCES
main.cpp
)
add_executable(aoc2023_day13 ${SOURCES})
target_compile_options(aoc2023_day13 PUBLIC -O2 -g)
target_link_libraries(aoc2023_day13 PUBLIC libc ban)
add_dependencies(aoc2023_day13 libc-install ban-install)
add_custom_target(aoc2023_day13-install
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/aoc2023_day13 ${BANAN_AOC2023_BIN}/day13
DEPENDS aoc2023_day13
DEPENDS aoc2023_always
)
add_dependencies(aoc2023 aoc2023_day13)
add_dependencies(aoc2023-install aoc2023_day13-install)

View File

@ -0,0 +1,142 @@
#include <BAN/Optional.h>
#include <BAN/String.h>
#include <BAN/Vector.h>
#include <ctype.h>
#include <inttypes.h>
#include <stdio.h>
using i8 = int8_t;
using i16 = int16_t;
using i32 = int32_t;
using i64 = int64_t;
using u8 = uint8_t;
using u16 = uint16_t;
using u32 = uint32_t;
using u64 = uint64_t;
using Grid = BAN::Vector<BAN::String>;
BAN::Optional<i64> calculate_score(const Grid& grid, i64 skip_val = 0)
{
// horizontal reflection
for (size_t x = 1; x < grid.front().size(); x++)
{
const size_t reflection_len = BAN::Math::min(x, grid.front().size() - x);
bool has_reflection = true;
for (size_t y = 0; y < grid.size() && has_reflection; y++)
for (size_t x_off = 0; x_off < reflection_len && has_reflection; x_off++)
if (grid[y][x - x_off - 1] != grid[y][x + x_off])
has_reflection = false;
if (has_reflection && (i64)x != skip_val)
return x;
}
// vertical reflection
for (size_t y = 1; y < grid.size(); y++)
{
const size_t reflection_len = BAN::Math::min(y, grid.size() - y);
bool has_reflection = true;
for (size_t x = 0; x < grid.front().size() && has_reflection; x++)
for (size_t y_off = 0; y_off < reflection_len && has_reflection; y_off++)
if (grid[y - y_off - 1][x] != grid[y + y_off][x])
has_reflection = false;
if (has_reflection && (i64)y * 100 != skip_val)
return y * 100;
}
return {};
}
i64 puzzle1(FILE* fp)
{
i64 result = 0;
char buffer[128];
while (fgets(buffer, sizeof(buffer), fp))
{
Grid grid;
while (buffer[0] != '\n')
{
ASSERT(buffer[strlen(buffer) - 1] == '\n');
buffer[strlen(buffer) - 1] = '\0';
MUST(grid.emplace_back(buffer));
if (fgets(buffer, sizeof(buffer), fp) == nullptr)
break;
}
result += *calculate_score(grid);
}
return result;
}
i64 puzzle2(FILE* fp)
{
i64 result = 0;
char buffer[128];
while (fgets(buffer, sizeof(buffer), fp))
{
Grid grid;
while (buffer[0] != '\n')
{
ASSERT(buffer[strlen(buffer) - 1] == '\n');
buffer[strlen(buffer) - 1] = '\0';
MUST(grid.emplace_back(buffer));
if (fgets(buffer, sizeof(buffer), fp) == nullptr)
break;
}
i64 original = *calculate_score(grid);
for (size_t y = 0; y < grid.size(); y++)
{
for (size_t x = 0; x < grid.front().size(); x++)
{
grid[y][x] = (grid[y][x] == '.') ? '#' : '.';
if (auto res = calculate_score(grid, original); res.has_value())
{
result += *res;
goto grid_done;
}
grid[y][x] = (grid[y][x] == '.') ? '#' : '.';
}
}
ASSERT_NOT_REACHED();
grid_done:
continue;
}
return result;
}
int main(int argc, char** argv)
{
const char* file_path = "/usr/share/aoc2023/day13_input.txt";
if (argc >= 2)
file_path = argv[1];
FILE* fp = fopen(file_path, "r");
if (fp == nullptr)
{
perror("fopen");
return 1;
}
printf("puzzle1: %" PRId64 "\n", puzzle1(fp));
fseek(fp, 0, SEEK_SET);
printf("puzzle2: %" PRId64 "\n", puzzle2(fp));
fclose(fp);
}

View File

@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 3.26)
project(aoc2023_day14 CXX)
set(SOURCES
main.cpp
)
add_executable(aoc2023_day14 ${SOURCES})
target_compile_options(aoc2023_day14 PUBLIC -O2 -g)
target_link_libraries(aoc2023_day14 PUBLIC libc ban)
add_dependencies(aoc2023_day14 libc-install ban-install)
add_custom_target(aoc2023_day14-install
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/aoc2023_day14 ${BANAN_AOC2023_BIN}/day14
DEPENDS aoc2023_day14
DEPENDS aoc2023_always
)
add_dependencies(aoc2023 aoc2023_day14)
add_dependencies(aoc2023-install aoc2023_day14-install)

View File

@ -0,0 +1,226 @@
#include <BAN/Array.h>
#include <BAN/HashMap.h>
#include <BAN/String.h>
#include <BAN/Swap.h>
#include <BAN/Vector.h>
#include <ctype.h>
#include <inttypes.h>
#include <stdio.h>
using i8 = int8_t;
using i16 = int16_t;
using i32 = int32_t;
using i64 = int64_t;
using u8 = uint8_t;
using u16 = uint16_t;
using u32 = uint32_t;
using u64 = uint64_t;
enum class Tile { Empty, RollableRock, StaticRock };
enum class Direction { North, South, West, East };
using Platform = BAN::Vector<BAN::Vector<Tile>>;
Platform parse_platform(FILE* fp)
{
Platform platform;
char buffer[128];
while (fgets(buffer, sizeof(buffer), fp))
{
BAN::StringView line(buffer);
ASSERT(line.back() == '\n');
line = line.substring(0, line.size() - 1);
if (line.empty())
continue;
auto char_to_tile =
[](char c)
{
if (c == '.')
return Tile::Empty;
if (c == 'O')
return Tile::RollableRock;
if (c == '#')
return Tile::StaticRock;
ASSERT_NOT_REACHED();
};
MUST(platform.emplace_back(line.size()));
for (size_t i = 0; i < line.size(); i++)
platform.back()[i] = char_to_tile(line[i]);
}
return platform;
}
void tilt_platform(Platform& platform, Direction direction)
{
switch (direction)
{
case Direction::North:
for (size_t y = 0; y < platform.size(); y++)
for (size_t x = 0; x < platform.front().size(); x++)
if (platform[y][x] == Tile::RollableRock)
for (size_t y_off = 1; y_off <= y && platform[y - y_off][x] == Tile::Empty; y_off++)
BAN::swap(platform[y - y_off + 1][x], platform[y - y_off][x]);
break;
case Direction::West:
for (size_t x = 0; x < platform.front().size(); x++)
for (size_t y = 0; y < platform.size(); y++)
if (platform[y][x] == Tile::RollableRock)
for (size_t x_off = 1; x_off <= x && platform[y][x - x_off] == Tile::Empty; x_off++)
BAN::swap(platform[y][x - x_off + 1], platform[y][x - x_off]);
break;
case Direction::South:
for (size_t y = platform.size(); y > 0; y--)
for (size_t x = 0; x < platform.front().size(); x++)
if (platform[y - 1][x] == Tile::RollableRock)
for (size_t y_off = 1; y + y_off - 1 < platform.size() && platform[y + y_off - 1][x] == Tile::Empty; y_off++)
BAN::swap(platform[y + y_off - 2][x], platform[y + y_off - 1][x]);
break;
case Direction::East:
for (size_t x = platform.front().size(); x > 0; x--)
for (size_t y = 0; y < platform.size(); y++)
if (platform[y][x - 1] == Tile::RollableRock)
for (size_t x_off = 1; x + x_off - 1 < platform.front().size() && platform[y][x + x_off - 1] == Tile::Empty; x_off++)
BAN::swap(platform[y][x + x_off - 2], platform[y][x + x_off - 1]);
break;
default:
ASSERT_NOT_REACHED();
}
}
i64 puzzle1(FILE* fp)
{
auto platform = parse_platform(fp);
tilt_platform(platform, Direction::North);
i64 result = 0;
for (size_t y = 0; y < platform.size(); y++)
for (size_t x = 0; x < platform.front().size(); x++)
if (platform[y][x] == Tile::RollableRock)
result += platform.size() - y;
return result;
}
struct PlatformKey
{
BAN::Array<uint64_t, BAN::Math::div_round_up(100 * 100, 64)> data;
bool operator==(const PlatformKey& other) const
{
return memcmp(data.data(), other.data.data(), data.size() * sizeof(decltype(data)::value_type)) == 0;
}
};
struct PlatformKeyHash
{
BAN::hash_t operator()(const PlatformKey& key) const
{
return key.data.back();
}
};
i64 puzzle2(FILE* fp)
{
auto platform = parse_platform(fp);
ASSERT(platform.size() * platform.front().size() == 100 * 100);
BAN::HashMap<PlatformKey, size_t, PlatformKeyHash> hit_cache;
auto build_cache_key =
[](const Platform& platform) -> PlatformKey
{
PlatformKey key;
for (size_t i = 0; i < 100 * 100; i++)
{
const size_t elem = i / 64;
const size_t bit = i % 64;
if (platform[i / 100][i % 100] == Tile::RollableRock)
key.data[elem] |= 1ull << bit;
}
return key;
};
PlatformKey cycle_begin_key;
i64 cycle_begin = -1;
i64 cycle_length = -1;
i64 missing_cycles = 1'000'000'000;
for (i64 i = 0; missing_cycles > 0; i++, missing_cycles--)
{
auto key = build_cache_key(platform);
if (cycle_begin == -1)
{
if (hit_cache.contains(key))
{
cycle_begin = i;
cycle_begin_key = key;
}
else
{
MUST(hit_cache.insert(key, i));
}
}
else
{
if (key == cycle_begin_key)
{
cycle_length = (i - hit_cache[key]) / 2;
break;
}
}
tilt_platform(platform, Direction::North);
tilt_platform(platform, Direction::West);
tilt_platform(platform, Direction::South);
tilt_platform(platform, Direction::East);
}
if (cycle_length != -1)
missing_cycles %= cycle_length;
for (; missing_cycles > 0; missing_cycles--)
{
tilt_platform(platform, Direction::North);
tilt_platform(platform, Direction::West);
tilt_platform(platform, Direction::South);
tilt_platform(platform, Direction::East);
}
i64 result = 0;
for (size_t y = 0; y < platform.size(); y++)
for (size_t x = 0; x < platform.front().size(); x++)
if (platform[y][x] == Tile::RollableRock)
result += platform.size() - y;
return result;
}
int main(int argc, char** argv)
{
const char* file_path = "/usr/share/aoc2023/day14_input.txt";
if (argc >= 2)
file_path = argv[1];
FILE* fp = fopen(file_path, "r");
if (fp == nullptr)
{
perror("fopen");
return 1;
}
printf("puzzle1: %" PRId64 "\n", puzzle1(fp));
fseek(fp, 0, SEEK_SET);
printf("puzzle2: %" PRId64 "\n", puzzle2(fp));
fclose(fp);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,100 @@
.OO.#.....#..O.O.O#....O....#....O....#..##.#...#..O....#.....O...##O...O#.O.#O.O....O......O.....OO
.......#........O...O..#.....O....O.#..#O#.#.##O.#O.#..O.O....O.###O.........#.O.O......O....#O.....
O..O.OOO....#O..#.#......OOO.#O#O.OO..#.O#.O.....O....#O.O####...##O..O....O#..O#...#..#......O..#..
.#.#.##...#.O.#...O##..O....#........#..#.#.O...O.......#.OO...#........#...O#.#.......O..OO.OO.OO##
....O.O.O#.....O#.OO..O...........O.OO..#.........O...O........#.OOOO..##.#....#.#..O..O#..O#.#O..OO
#.#...O##.##...#.#O.OO..O.##O.#.##O...#O#....#OOOO.O........#......O.....O#....O....O..O#.OO#.O.....
......O..O.O...O...#..OOO##...O.OO....#......OO.#.....OOO##..##.#.#..#OOO.......#O.#....O....O...#.#
O..........OOO#.O.O.#.....OO...O.#..#...OO....OO#O.#OOO..OO..#.OO.....#O##.O..#.OOO...#.#O#...#.OO..
....O..#O...........#.O..OO##....#..................O....O.O.O.#.O.......O.O.#.....O..#O..#..#......
....##OO.#.....#OO...O......O#.....O.OO#....O..O.###.##.OO.#O....#.OO#O#.O......##O#..O...O#.#.O..O.
#....OO.##...#.....O.O....#O.O#...OO#..#.O.O.#..O......###.O..O......#..........O...#.O.#O#OO..O.O.#
........#....O..O###.#.#...OO##.#.....#O.O..........#...O..O...OO##....O#..O.#O......#..O..O...#..O#
..O.#..O......#......O..#......#...O.#O...O...O.#..#.OO.O.O...O...O.O.#....#O...O#..##..O..O......O.
...OO.............O....#O.O..O...OOO....O..#...O....#..#.O#.#..OO..#......O.....OOO....O..##..O#....
O.#O#.#O#.#..#.O.#....#..O...#..O#O..#.#.O..O...OO.....O...O....#.O.#OO.OOO#.O.....O..O#.O..#.OO##..
.....#..#.O.O##O.#O..O..OO.#O..O....O..O.....O..#.##..O.............#......O...###...O.....O....#...
O...O..O#O..#O.#O.O#.O...#O#.......O.......#...O.#.#.O#.......O.#.OO.O.#O..O..........O#...O.OO..#..
..O.O#.O.....O.OO..O#O...#.#....#..O.OO.....O.........O.O#...#....##......OO............O.O......#..
...O#OO.....OOO.........#OOO.O.......O.....O....O....#OO##.O##...#.................O....#O...#......
#.O#OO.#..#.......O.O.###O....O..O.....#.O.OOO.#.O#.#..O....#..#...#.O#.O#..##..#...##OO..O#...#...#
O...#.......O##.OO...#O..#......O.......O..#.....#..#.#..OO....#.#.#O..OO..#.OO.#..##OO#.O#OO..#...#
...O...#...O.#O....#.......O..OO.O..O.......O.#....#O..#O#.#...#....#.#....#....O.O......O.O........
#..O...OOOO.......#.....O..O...##O...#OO...OO..OO#O.O..#.....#O...#..#.....O.#O.#...#....#...O#....#
.O.#O.....#...#...O.OOO#...O#..##O#OO##.#.#...O#.....#O..O#..#OO....#O...O.#..O..#.....#.#.OO...O.#.
..#..OO......#..O..#.O.O..O.#...#.#....#.......O.#O#....O.#.O#.O.O.O..O.###O.OOO...#O....O#..O.#...#
....O.#.....#.....OO.......O#O.O..OO#..O.O...OOOO....OO..O.#..O.....OOO.O#O.O......#..#O.O....O.#..O
..#......#O##....O.O.OO..#.#.....O.#.#O#.....OO#......###..#.O.O#....#O...O...O...OO...OO..O.#...O.O
#...#.....O.#..##.......#.OO......#O..O.........O#.#O#.#..#O.O......O.O..O.OO#...O.#..O...O..O.O....
O.#..##...O.O..O....O.O.OO......O...O.O....#..#.O.....OO...O.OO.O....O....O.O.O........O..O#...OO.O.
....O..O.O...OO#O..O..........OO#...O..O.O#...#O......#.#.#...#..O.....O..O.#O#.....O.#O.O..O#.....O
#O...O...O..O.#.O#OO.......O#.....O.#..O.OO.#.........O..OO.....OO.OO#OO...O.#.O....#...#..#.##.O...
##O...#.##...OO.......##..#.#.#..OO.....OO#.....OOO.OO.......O.#.O...#..O.OO.#.....##.#.#........#.#
...OO..#....#..O...O........OO.O...##.......O...#O#..O...#....#O....#....#O..##.#O.#..O.....##OO#.O.
.O.#.O#O.#...#..O.##.#....OO..........O.#.O.O#O...#......O.O....O#.O.#.....O...##...O.#.O.O.#O##.O..
...#.....O....#.OO.#.#..#..#..###.....#.......#.O...O#...O..#.O#O##O.O....#.O.#....O............O...
#.##..##....#...O.....#.O.....OO....OOO......#...O.#....OO.#....O...##..#OO.#....O..O.O#O.O..#...O#.
O##....OO...#.#..O...#......#.##..O......#........O.O.O.#O..#.#..O..#.....#O.O.....##OO.O..O..#.....
.....O#..O.#.#..#O#.O.......OO.O.....#.....##...O..#...OO.O.OO....OOO........O#OO...O..#.OO##OO#.O#.
...#.O#O......O....O#................#O.....#..O.OO#.......#.#.O##......OO.#..#..O#...OO..#.#.O#O.#.
O......#.#....O.O...O.#.O......#.#.O..OOO.O..........#..O....#..O.O.#.O.##OO..#O#.O....#O..###...#..
..##..#...#..#.#O.#.#..#O#.#.....O.#..#OO....OOO#O...O##O..#.##.O..O..O#O..........O.#..#....O#.....
O.....#OO#...OO#O..O......#O.O..........O####.#.....#..#.....#...#..O#...#...#O#...#..O..#.O#......O
....O###.OOO...O.O...O#...O##....OO..O..O#..O.#.OO.O#.#...#O.#......##.O.......O...#.O.O.#.......O.#
O..O.....##....#OO#.OO....##.O#.O..#.#.......O......#....#O#.O.......OO..O.#..#.O..#..........OO..OO
.......O.O......O.....#O..#.....#....#O#...#...O..#...O#....#O.O.....O...#OO...#......#...##...OOO..
#..O.....#O.O..O.O...#.O...O.O.#.#OO.....OOO....O.........OO..O.OO#...O#..O...#.#..#.##.#..OO...#...
...O..OO.#O...OOOOOO..O#....#.OO...O#...O.....#.#.O.....O..O.O#O.#.#.O..#....O..#.#...OO#.......#O..
..............O.OO#.........##..............O..#........##....O...#.#.##O#.O....O#.OO....O.#O.OO.O.O
....O....#..O..O....O##...OO...O....O#..#O#.....OO...#.OOO#....#.O..O..O...#.....#..OO#.#O....O.O#..
#O#...O..O#.OO..OO....#.......O#.........##.....OOO......O..#....O.#.#.O.O..O.O..O...#.#..O.#......O
O....O...#..##...OO.....#.#.#.........OO..#..#...O...O..#..#.OOOO....#O..O##.O.O#....#....O.##.O..O.
....O..#.OO.##...O.OOO.O.O#.....#..O..O....#.#O...#O#O.....O..##...##.O.#O...#.....OO.#..#O#.O.#.O..
O..O#.....O..OO........OO...##....O#OO#.O..OO.....O.....O...O.##..#..#O..O.#.OO....#.........O...#.O
#...O.O..O..O........#OOOO......#..O...#..O.....OO#.....O.......O#...O..O.....##O.....OOO#...#..##O.
#.##...##O.#....#.OO#.OO.#..OO#...#...O..O.O#..##..O.....O.OO.#OO#....#.#.O.#...OO..#.##O...#.....#O
..#.O.....O##.....O.O#O.#O#..#...O...O#.......#O..OO.O..O..O.##O...O...........#..#....O............
..O.#O....O...#O....#....O...OO.OO...#O.O..O.....O.........##.....#..#O.O.OO..O..#....#.............
..#...O.OO.......#....OO.O...#.O...#O.O#......O..#..............#O......#.O#..#...O.......#......O..
..O..O....O.....O#O....OO.....O.O##O...O....#..OO...O.OO....O.O...O.O.......#..#O.O..O.........#.#..
...O..O#.......#..#...OOOO...O.###..#O.OO..O#.O.O.OOO.#.O..O.#......#OO....O....#..#O..O......O....O
....##OO###.......OO.##O.....##.#.OOOO.#OO#...#.#.O..#.....O.#.##O..#OOO.#OO.OO#OO.......O.#...#O...
....O.O.OO.O.#O...#.O.O.....OO#.OO....O.......O.O.O...##OO....O....#.#..#O###...OO..O...#.#.O.O.O.#.
....O....O....#...O..O#.OO..O.OOO...#.......###O#....##O.O#..O...O.#.O.O.......O.###..O#.#.O.O.....#
..#..O...O..O.......O..#.#O.O..O...O.........#.O......#....O..O..O#.O..O..#.O.......#...#...O#.OO..#
O....OO#O..#....O.#OOO..O.....O...O.O....O.O#O..#........OOO..#O..O.O.....O#O...#.#.....#.O.O......#
##OO.O#..#O#.....O.........O..O.#.......OO.....O...#........###.OO...O..#..OO.O#.O#O....#..##....O.O
.#OOO........O#...O.#..O.O.O..#.....#..OOO...#.....O........#.........O.O.....#.#.O.O#O.#.#...#...#.
.O.....#.#....O.O...#O..O###...O..#...#O#.O.O...........#.O....#O.OO......O....O..OO....#....#..##O#
....#..O#...#....#.O..O.#......OOO.........O....#....O...O#......#.#...OO..O..#.#O.#......#.O.#.#...
O.O.......O.O#OOO#.O......O#.O......OO.O..O.......##O....O...#.O...O..#....###.####...OO...OO..O..OO
#..#.....O.....O..O.#....#O....O.OO..#...O.O.....O.O..#O...O#....O##...O.....#OO#..OO#..#.O..O.###..
O##..#...O..#...........OO...#..#....#...#.O.....OOO...O.OO...O.#.#..O...OO.....OO#O...#O...#..#...O
....O....#.....O.#.#.OO..#.........#..........O#..O#O.#....###..O.....#.......O..#.#.....OOOO.......
O.O.O.#........#.#.O.#..##O.OO..O...#O#OO.....O...O.#.#.O#O......#O.....O...O..O##.##.#......O..#...
....O.......O..#.OO###..#.O#....#O.OO#..OO...#.#...###...#OO#..#...........#OO#..O.#..#....O#....O.O
....#O..#...#O..O..O...#....O....#....OOO.OO.O..#...OO.O#.....OO.O..#..#...O.#.#.O..#.#O.O#O.O......
.OO....#.OO.............#..###.#...O...#O...OO#...OO#O..#...##.#..........O..#...#.....O.....O...O..
#.O....#O....O#..#O......#####O....O..O....O#.......#..O.#..#....O...##..#.........O..O.....#.O#.O..
...O.O.....#....O.#O....#....O..........###....O.#.O.O#O.#.#.O..#.O.O...#.###...OOO...##..O.#.O..O#.
.##...#..O..O.#....O..OO..O.....#..O.##O#O.........OO#...#.......O#....O............#O.##.OO#..O.#..
.O.........O.#O.......#O..O....#.##.O.O...##....#.##...#O#.#.....O#O.#...#..O.#.O...O..##..O.#.O.O..
...O.#.O.O#.OO..OO#.O...O.O.O...O.O.###O.O..O.O.O........#.O....#....O.#.#OO.....OO.#.....##..#OO...
....OO##...#O.#..#.....#.#..##O.O##..O...OO#O...........O..O.#.OO..O...OO.##.O.#..#.##OO...#..O.....
...#....O....#..#.O....O..#....O...#....#O...O.O..#O#O..O.......#..O......#OO..OO.OO......#.##O...O.
...O......O.....OO#.O.#..O..O..O.O.....O...........O....#.###...O.#......O....O.##..#....O...#.#.#..
..O.O.#.O......OO..##.O......O#..#O.....##O.O#....#.#....O.....OO.#.....O.O#.#..O...O.#....O#...O..#
O...OO#O.......#..#O#........#.#.##.O........#O...#..#O..#.....#....O#O.#......##O....O.OO.O........
.#..O.....OO..O.#..O.O........O.......#.#.#.....O.OO.....#......O.......O##.O...#....#O....O........
.O.OO....O.O.##..........O.....O.##.#.......OO....O.O....#........##O..O.O##.O......#.#.#.....OO.O#.
O.....OOO.O###...OO.##.............#..O.#.....#O.....O..OO..O#....##....OO.....#.O.#OO......OO...O##
O........###.##..O......O#.....O.O#O......#....O#..O........O..O.#.......OO......O..##..OOO.O..O.O#.
OO.#O......OO...O..O..#..##O..#...O......#..............O..............O..#......#.#O....O...#...O..
.O..O#.O#..O.#..O..OO#..#.##O.....O.O.#......##O#..O....O......#.#.O..#.....O#.O..O.#.##.......#....
....OO.#....O..O...OO..#O..#...###..OO.......O##.##.#OO..OOO...#.O..O......O........##..#...OO...#..
....#O........#.O......O##...##O......#.O..O.O........##...#.O.......O..#OOOOO..#......O###..O..O.O.
#.#..O.#.........O#...#O.##..O.....#..O...........##......O..#...O......#.................OO...#O.#.
..O..#OO...OOO...O.#...#.......#OOOO#...#...O....O..#........OO....O..O..##.OO.O#....OO#O#..#....O..
.O.O..........OOOO.#.#......O##...............#.O.OO.###.#....O..O.O#O...OO..O#O........#.O.....O#.O
...#......#.#.....#........#O#.........#O....#O.O..#.###.....O#OOO#O#.#.O..#.#OOO..O.#....#O......#.
O..O...#O.O.....O..O.#.#......O.#O#...#.....#..OO#.....OO.O#.O.....#....O.#...O...O.OO.....O....O#..