chore: run pre-commit

Signed-off-by: Matej Focko <me@mfocko.xyz>
This commit is contained in:
Matej Focko 2024-01-03 19:38:35 +01:00
parent e63dbba74a
commit 2cf4a3efba
Signed by: mfocko
GPG key ID: 7C47D46246790496
36 changed files with 647 additions and 478 deletions

View file

@ -29,21 +29,22 @@ any variables, we can use just backtracking and recursion.
Let's start with some brainstorming of the solution. Let's start with some brainstorming of the solution.
* How will I know what I've checked without any variables? - How will I know what I've checked without any variables?
* _answer_: recursion will need to take care of that, cause I'm not allowed - _answer_: recursion will need to take care of that, cause I'm not allowed
anything else anything else
* How will I pass around the fact I've found the exit? - How will I pass around the fact I've found the exit?
* _answer_: I can return values from helper functions, so I should be able to - _answer_: I can return values from helper functions, so I should be able to
indicate _found_/_not found_ indicate _found_/_not found_
* How is the exit marked? - How is the exit marked?
* _answer_: there is one “beeper” as a mark - _answer_: there is one “beeper” as a mark
* Can I reduce my problem somehow? - Can I reduce my problem somehow?
* _answer_: I could check each possible direction as a reduced search space - _answer_: I could check each possible direction as a reduced search space
## »Rough« pseudocode ## »Rough« pseudocode
We should be able to construct a _skeleton_ of our solution at least. Pseudocode We should be able to construct a _skeleton_ of our solution at least. Pseudocode
follows: follows:
```ruby ```ruby
def find_exit def find_exit
if found the exit then if found the exit then
@ -67,6 +68,7 @@ In the proper pseudocode we will need to dive into the technical details like
the way we check for exit, move around, etc. the way we check for exit, move around, etc.
We can start by cleaning up and decomposing the function written above: We can start by cleaning up and decomposing the function written above:
```ruby ```ruby
def find_exit def find_exit
# BASE: found exit # BASE: found exit
@ -117,11 +119,13 @@ end
``` ```
We are missing few of the functions that we use in our pseudocode above: We are missing few of the functions that we use in our pseudocode above:
* `found_exit()`
* `turn_around()` - `found_exit()`
* `turn_right()` - `turn_around()`
- `turn_right()`
We can implement those easily: We can implement those easily:
```ruby ```ruby
def found_exit def found_exit
if not beepers_present() then if not beepers_present() then
@ -154,6 +158,7 @@ Now we have everything ready for implementing it in Python.
## Actual implementation ## Actual implementation
It's just a matter of rewriting the pseudocode into Python[^1]: It's just a matter of rewriting the pseudocode into Python[^1]:
```py ```py
class SuperKarel(Karel): class SuperKarel(Karel):
# you can define your own helper functions on Karel here, if you wish to # you can define your own helper functions on Karel here, if you wish to
@ -223,6 +228,7 @@ class SuperKarel(Karel):
We have relatively repetitive code for checking each of the directions, I would We have relatively repetitive code for checking each of the directions, I would
propose to refactor a bit, in a fashion of checkin just forward, so it's more propose to refactor a bit, in a fashion of checkin just forward, so it's more
readable: readable:
```py ```py
def find_exit(self) -> bool: def find_exit(self) -> bool:
if self.found_exit(): if self.found_exit():
@ -249,6 +255,7 @@ We can also notice that turning around takes 2 left turns and turning to right
does 3. We get 5 left turns in total when we turn around and right afterwards… does 3. We get 5 left turns in total when we turn around and right afterwards…
Taking 4 left turns just rotates us back to our initial direction, therefore it Taking 4 left turns just rotates us back to our initial direction, therefore it
is sufficient to do just one left turn (`5 % 4 == 1`). That way we get: is sufficient to do just one left turn (`5 % 4 == 1`). That way we get:
```py ```py
def find_exit(self) -> bool: def find_exit(self) -> bool:
if self.found_exit(): if self.found_exit():
@ -280,6 +287,7 @@ solution that we haven't spoken about.
We are silently expecting the maze **not to** have any loops. Example of such We are silently expecting the maze **not to** have any loops. Example of such
maze can be the `maze666.kw`: maze can be the `maze666.kw`:
``` ```
┌─────────┬─┐ ┌─────────┬─┐
│. . . . .│.│ │. . . . .│.│
@ -298,6 +306,7 @@ maze can be the `maze666.kw`:
If you try running our solution on this map, Karel just loops and never finds If you try running our solution on this map, Karel just loops and never finds
the solution. Let's have a look at the loop he gets stuck in: the solution. Let's have a look at the loop he gets stuck in:
``` ```
┌─────────┬─┐ ┌─────────┬─┐
│* * * * *│.│ │* * * * *│.│
@ -332,6 +341,7 @@ to mark the “cells” that we have tried. We can easily use beepers for this,
we need to be careful **not to** confuse the exit with already visited cell. we need to be careful **not to** confuse the exit with already visited cell.
To do that we'll use **2** beepers instead of the one. Implementation follows: To do that we'll use **2** beepers instead of the one. Implementation follows:
```py ```py
def visited(self) -> bool: def visited(self) -> bool:
if not self.beepers_present(): if not self.beepers_present():

View file

@ -6,10 +6,10 @@ description: |
Solving the shortest path problem with a naïve approach that turns into Solving the shortest path problem with a naïve approach that turns into
something. something.
tags: tags:
- cpp - cpp
- brute force - brute force
- bellman ford - bellman ford
- dynamic programming - dynamic programming
last_update: last_update:
date: 2024-01-01 date: 2024-01-01
--- ---
@ -33,6 +33,7 @@ highest possible price) and try to improve what we've gotten so far until there
are no improvements. That sounds fine, we shall implement this. Since we are are no improvements. That sounds fine, we shall implement this. Since we are
going on repeat, we will name this function `bf()` as in _brute-force_, cause it going on repeat, we will name this function `bf()` as in _brute-force_, cause it
is trying to find it the hard way: is trying to find it the hard way:
```cpp ```cpp
const static std::vector<vertex_t> DIRECTIONS = const static std::vector<vertex_t> DIRECTIONS =
std::vector{std::make_pair(0, 1), std::make_pair(0, -1), std::vector{std::make_pair(0, 1), std::make_pair(0, -1),
@ -106,6 +107,7 @@ finding the path `u → x1 → … → xn → v` to subproblems
_Is our solution correct?_ It appears to be correct… We have rather complicated _Is our solution correct?_ It appears to be correct… We have rather complicated
map and our algorithm has finished in an instant with the following output: map and our algorithm has finished in an instant with the following output:
``` ```
Normal cost: 1 Normal cost: 1
Vortex cost: 5 Vortex cost: 5
@ -130,6 +132,7 @@ one path skipping the `*` cells, since they cost more than going around.
We can play around a bit with it. The `*` cells can be even vortices that pull We can play around a bit with it. The `*` cells can be even vortices that pull
you in with a negative price and let you _propel_ yourself out :wink: Let's you in with a negative price and let you _propel_ yourself out :wink: Let's
change their cost to `-1` then. Let's check what's the fastest path to the cell. change their cost to `-1` then. Let's check what's the fastest path to the cell.
``` ```
Normal cost: 1 Normal cost: 1
Vortex cost: -1 Vortex cost: -1
@ -175,6 +178,7 @@ the repeating patterns algorithmically.
On the other hand, we can approach this from the different perspective. Let's On the other hand, we can approach this from the different perspective. Let's
assume the worst-case scenario (generalized for any graph): assume the worst-case scenario (generalized for any graph):
> Let $K_n$ be complete graph. Let $P$ be the shortest path from $v_1$ to $v_n$ > Let $K_n$ be complete graph. Let $P$ be the shortest path from $v_1$ to $v_n$
> such that $P$ has $n - 1$ edges, i.e. the shortest path between the two chosen > such that $P$ has $n - 1$ edges, i.e. the shortest path between the two chosen
> vertices visits all vertices (not necessarily in order) and has the lowest > vertices visits all vertices (not necessarily in order) and has the lowest
@ -194,6 +198,7 @@ assume the worst-case scenario (generalized for any graph):
_How can we leverage this?_ We will go through the edges only as many times as _How can we leverage this?_ We will go through the edges only as many times as
cells we have. Let's adjust the code to fix the looping: cells we have. Let's adjust the code to fix the looping:
```cpp ```cpp
auto bf_finite(const graph& g, const vertex_t& source, auto bf_finite(const graph& g, const vertex_t& source,
const vertex_t& destination) -> int { const vertex_t& destination) -> int {
@ -242,6 +247,7 @@ auto bf_finite(const graph& g, const vertex_t& source,
``` ```
And we get the following result: And we get the following result:
``` ```
Normal cost: 1 Normal cost: 1
Vortex cost: -1 Vortex cost: -1
@ -291,6 +297,7 @@ exception that it doesn't report whether there are any negative cycles, it just
ends. ends.
Let's have a look at a proper implementation of the Bellman-Ford algorithm: Let's have a look at a proper implementation of the Bellman-Ford algorithm:
```cpp ```cpp
auto bellman_ford(const graph& g, const vertex_t& source) auto bellman_ford(const graph& g, const vertex_t& source)
-> std::vector<std::vector<int>> { -> std::vector<std::vector<int>> {
@ -364,6 +371,7 @@ auto bellman_ford(const graph& g, const vertex_t& source)
``` ```
And if we run it with our negative cost of entering vortices: And if we run it with our negative cost of entering vortices:
``` ```
[Bellman-Ford] Found a negative loop [Bellman-Ford] Found a negative loop
[Bellman-Ford] Cost: -240 [Bellman-Ford] Cost: -240
@ -399,12 +407,14 @@ Let's have a short look at the time complexities of the presented algorithms:
1. naïve approach: given that there are no negative loops, we are bound by the 1. naïve approach: given that there are no negative loops, we are bound by the
worst-case ordering of the relaxations which results in worst-case ordering of the relaxations which results in
$$ $$
\mathcal{O}(\vert V \vert \cdot \vert E \vert) \mathcal{O}(\vert V \vert \cdot \vert E \vert)
$$ $$
2. our naïve approach with the fixed count of iterations instead of the 2. our naïve approach with the fixed count of iterations instead of the
`do-while` loop results in the same worst-case time complexity: `do-while` loop results in the same worst-case time complexity:
$$ $$
\Theta(\vert V \vert \cdot \vert E \vert) \Theta(\vert V \vert \cdot \vert E \vert)
$$ $$
@ -418,6 +428,7 @@ Let's have a short look at the time complexities of the presented algorithms:
Since we are literally copy-pasting the body of the loops just for the sake of Since we are literally copy-pasting the body of the loops just for the sake of
relaxing, we can factor that part out into a separate function: relaxing, we can factor that part out into a separate function:
```cpp ```cpp
static auto _check_vertex(const graph& g, static auto _check_vertex(const graph& g,
std::vector<std::vector<int>>& distances, int v, std::vector<std::vector<int>>& distances, int v,
@ -461,6 +472,7 @@ there would be any edge relaxed instead of performing the relaxation itself.
Then we can also see the differences between the specific versions of our Then we can also see the differences between the specific versions of our
path-finding algorithms in a clear way: path-finding algorithms in a clear way:
```cpp ```cpp
auto bf(const graph& g, const vertex_t& source, const vertex_t& destination) auto bf(const graph& g, const vertex_t& source, const vertex_t& destination)
-> int { -> int {
@ -566,8 +578,9 @@ consider it a brute-force algorithm.
::: :::
[^1]: [Breadth-first search](https://en.wikipedia.org/wiki/Breadth-first_search) [^1]: [Breadth-first search](https://en.wikipedia.org/wiki/Breadth-first_search)
[^2]: Of course, there are some technicalities like keeping track of the visited [^2]:
Of course, there are some technicalities like keeping track of the visited
vertices to not taint the shortest path by already visited vertices. vertices to not taint the shortest path by already visited vertices.
[^3]: or at least you should, LOL [^3]: or at least you should, LOL

View file

@ -5,10 +5,10 @@ title: Dijkstra's algorithm
description: | description: |
Moving from Bellman-Ford into the Dijsktra's algorithm. Moving from Bellman-Ford into the Dijsktra's algorithm.
tags: tags:
- cpp - cpp
- dynamic programming - dynamic programming
- greedy - greedy
- dijkstra - dijkstra
last_update: last_update:
date: 2024-01-03 date: 2024-01-03
--- ---
@ -46,6 +46,7 @@ I'll start with a well-known meme about Dijkstra's algorithm:
![Dijkstra's algorithm meme](/img/algorithms/paths/bf-to-astar/dijkstra-meme.jpg) ![Dijkstra's algorithm meme](/img/algorithms/paths/bf-to-astar/dijkstra-meme.jpg)
And then follow up on that with the actual backstory from Dijkstra himself: And then follow up on that with the actual backstory from Dijkstra himself:
> What is the shortest way to travel from Rotterdam to Groningen, in general: > What is the shortest way to travel from Rotterdam to Groningen, in general:
> from given city to given city. It is the algorithm for the shortest path, > from given city to given city. It is the algorithm for the shortest path,
> which I designed in about twenty minutes. One morning I was shopping in > which I designed in about twenty minutes. One morning I was shopping in
@ -75,6 +76,7 @@ monotonically non-decreasing changes in the costs of shortest paths.
## Short description ## Short description
Let's have a brief look at the pseudocode taken from the Wikipedia: Let's have a brief look at the pseudocode taken from the Wikipedia:
``` ```
function Dijkstra(Graph, source): function Dijkstra(Graph, source):
for each vertex v in Graph.Vertices: for each vertex v in Graph.Vertices:
@ -141,6 +143,7 @@ Firstly we need to have some priority queue wrappers. C++ itself offers
functions that can be used for maintaining max heaps. They also have generalized functions that can be used for maintaining max heaps. They also have generalized
version with any ordering, in our case we need reverse ordering, because we need version with any ordering, in our case we need reverse ordering, because we need
the min heap. the min heap.
```cpp ```cpp
using pqueue_item_t = std::pair<int, vertex_t>; using pqueue_item_t = std::pair<int, vertex_t>;
using pqueue_t = std::vector<pqueue_item_t>; using pqueue_t = std::vector<pqueue_item_t>;
@ -165,6 +168,7 @@ auto popq(pqueue_t& q) -> std::optional<pqueue_item_t> {
And now we can finally move to the actual implementation of the Dijkstra's And now we can finally move to the actual implementation of the Dijkstra's
algorithm: algorithm:
```cpp ```cpp
auto dijkstra(const graph& g, const vertex_t& source) auto dijkstra(const graph& g, const vertex_t& source)
-> std::vector<std::vector<int>> { -> std::vector<std::vector<int>> {
@ -220,6 +224,7 @@ structure.
The original implementation doesn't leverage the heap which results in The original implementation doesn't leverage the heap which results in
repetitive _look up_ of the “closest” vertex, hence we get the following repetitive _look up_ of the “closest” vertex, hence we get the following
worst-case time complexity in the _Bachmann-Landau_ notation: worst-case time complexity in the _Bachmann-Landau_ notation:
$$ $$
\Theta(\vert V \vert^2) \Theta(\vert V \vert^2)
$$ $$
@ -227,6 +232,7 @@ $$
If we turn our attention to the backing data structure, we always want the If we turn our attention to the backing data structure, we always want the
“cheapest” vertex, that's why we can use the min heap, given that we use “cheapest” vertex, that's why we can use the min heap, given that we use
Fibonacci heap we can achieve the following amortized time complexity: Fibonacci heap we can achieve the following amortized time complexity:
$$ $$
\mathcal{O}(\vert E \vert + \vert V \vert \cdot \log{\vert V \vert}) \mathcal{O}(\vert E \vert + \vert V \vert \cdot \log{\vert V \vert})
$$ $$
@ -242,6 +248,7 @@ min or max).
## Running the Dijkstra ## Running the Dijkstra
Let's run our code: Let's run our code:
``` ```
Normal cost: 1 Normal cost: 1
Vortex cost: 5 Vortex cost: 5
@ -273,6 +280,7 @@ infinitely when you have negative weights or loops in the graph. Well, if we use
our _propelling vortices_, not only we have the negative weights, but also the our _propelling vortices_, not only we have the negative weights, but also the
negative loops. Let's run our code! Our first naïve approach was actually negative loops. Let's run our code! Our first naïve approach was actually
looping: looping:
``` ```
Normal cost: 1 Normal cost: 1
Vortex cost: -1 Vortex cost: -1

View file

@ -5,9 +5,9 @@ title: A* algorithm
description: | description: |
Moving from Dijkstra's algorithm into the A* algorithm. Moving from Dijkstra's algorithm into the A* algorithm.
tags: tags:
- cpp - cpp
- dynamic programming - dynamic programming
- astar - astar
last_update: last_update:
date: 2024-01-03 date: 2024-01-03
--- ---
@ -15,6 +15,7 @@ last_update:
## Intro ## Intro
Let's start by the recap of what we've achieved so far: Let's start by the recap of what we've achieved so far:
1. We have implemented a naïve brute-force algorithm that tries to relax paths 1. We have implemented a naïve brute-force algorithm that tries to relax paths
as long as there are any paths to be relaxed. as long as there are any paths to be relaxed.
2. Then we have fixed an issue caused by negative loops that can result in 2. Then we have fixed an issue caused by negative loops that can result in
@ -43,9 +44,9 @@ The important question here is how to _influence_ the algorithm, so that it does
choose the path that _makes more sense_ rather than the one that costs the choose the path that _makes more sense_ rather than the one that costs the
least. least.
## A* description ## A\* description
The _A* algorithm_ can be considered a modification of Dijkstra's algorithm. The The _A\* algorithm_ can be considered a modification of Dijkstra's algorithm. The
cost is still the same, we cannot change it, right? However when we pick the cost is still the same, we cannot change it, right? However when we pick the
vertices from the heap, we can influence the order by some _heuristic_. In this vertices from the heap, we can influence the order by some _heuristic_. In this
case, we introduce a function that can suggest how feasible the vertex is. case, we introduce a function that can suggest how feasible the vertex is.
@ -66,6 +67,7 @@ road makes us 50 km away and using the other road we will be 200 km away.
Our map is a bit simpler, but we can use a very similar principle. We will use Our map is a bit simpler, but we can use a very similar principle. We will use
the _Manhattan distance_, which is defined in a following way: the _Manhattan distance_, which is defined in a following way:
$$ $$
\vert x_a - x_b \vert + \vert y_a - y_b \vert \vert x_a - x_b \vert + \vert y_a - y_b \vert
$$ $$
@ -81,6 +83,7 @@ calculate the shortest path and pass the heuristic as a parameter.
## Implementation ## Implementation
Actual implementation is very easy once we have the Dijkstra's algorithm: Actual implementation is very easy once we have the Dijkstra's algorithm:
```cpp ```cpp
auto astar(const graph& g, const vertex_t& source, const auto& h) auto astar(const graph& g, const vertex_t& source, const auto& h)
-> std::vector<std::vector<int>> { -> std::vector<std::vector<int>> {
@ -132,6 +135,7 @@ auto astar(const graph& g, const vertex_t& source, const auto& h)
## Running on our map ## Running on our map
For this algorithm I will also show the example of a call: For this algorithm I will also show the example of a call:
```cpp ```cpp
distances = astar(g, std::make_pair(1, 9), [](const auto& u) { distances = astar(g, std::make_pair(1, 9), [](const auto& u) {
auto [x, y] = u; auto [x, y] = u;
@ -145,6 +149,7 @@ source vertex where we start. And finally the lambda returns
_Manhattan distance_ to the goal. _Manhattan distance_ to the goal.
And we get the following result: And we get the following result:
``` ```
Normal cost: 1 Normal cost: 1
Vortex cost: 5 Vortex cost: 5
@ -171,6 +176,7 @@ Graph:
Now you may wonder how does it compare to the previous algorithms. Supposedly it Now you may wonder how does it compare to the previous algorithms. Supposedly it
should be faster. Let's add counters and debugging output when we update should be faster. Let's add counters and debugging output when we update
distance to our goal. And now if we run our code, we get the following output: distance to our goal. And now if we run our code, we get the following output:
``` ```
Normal cost: 1 Normal cost: 1
Vortex cost: 5 Vortex cost: 5

View file

@ -5,13 +5,13 @@ title: From BF to A*
description: | description: |
Figuring out shortest-path problem from the BF to the A* algorithm. Figuring out shortest-path problem from the BF to the A* algorithm.
tags: tags:
- cpp - cpp
- brute force - brute force
- bellman ford - bellman ford
- dynamic programming - dynamic programming
- dijkstra - dijkstra
- greedy - greedy
- a star - a star
last_update: last_update:
date: 2024-01-01 date: 2024-01-01
--- ---
@ -24,6 +24,7 @@ algorithms, we will use a 2D map with some rules that will allow us to show cons
and pros of the shown algorithms. and pros of the shown algorithms.
Let's have a look at the example map: Let's have a look at the example map:
``` ```
############# #############
#..#..*.*.**# #..#..*.*.**#
@ -39,22 +40,26 @@ Let's have a look at the example map:
``` ```
We can see three different kinds of cells: We can see three different kinds of cells:
1. `#` which represent walls, that cannot be entered at all 1. `#` which represent walls, that cannot be entered at all
2. `*` which represent vortices that can be entered at the cost of 5 coins 2. `*` which represent vortices that can be entered at the cost of 5 coins
3. `.` which represent normal cells that can be entered for 1 coin (which is the 3. `.` which represent normal cells that can be entered for 1 coin (which is the
base price of moving around the map) base price of moving around the map)
Let's dissect a specific position on the map to get a better grasp of the rules: Let's dissect a specific position on the map to get a better grasp of the rules:
``` ```
. .
#S* #S*
. .
``` ```
We are standing in the cell marked with `S` and we have the following options We are standing in the cell marked with `S` and we have the following options
* move to the north (`.`) with the cost of 1 coin,
* move to the west (`#`) **is not** allowed because of the wall, - move to the north (`.`) with the cost of 1 coin,
* move to the east (`*`) is allowed with the cost of 5 coins, and finally - move to the west (`#`) **is not** allowed because of the wall,
* move to the south (`.`) with the cost of 1 coin. - move to the east (`*`) is allowed with the cost of 5 coins, and finally
- move to the south (`.`) with the cost of 1 coin.
:::info :::info
@ -67,13 +72,15 @@ Further on I will follow the same scheme for marking cells with an addition of
For working with this map I have prepared a basic structure for the graph in C++ For working with this map I have prepared a basic structure for the graph in C++
that will abstract some of the internal workings of our map, namely: that will abstract some of the internal workings of our map, namely:
* remembers the costs of moving around
* provides a simple function that returns price for moving **directly** between - remembers the costs of moving around
- provides a simple function that returns price for moving **directly** between
two positions on the map two positions on the map
* allows us to print the map out, just in case we'd need some adjustments to be - allows us to print the map out, just in case we'd need some adjustments to be
made made
We can see the `graph` header here: We can see the `graph` header here:
```cpp ```cpp
#ifndef _GRAPH_HPP #ifndef _GRAPH_HPP
#define _GRAPH_HPP #define _GRAPH_HPP

View file

@ -3,14 +3,14 @@ title: How can Copr help with broken dependencies
description: Copr comes to save you when maintainer doesn't care. description: Copr comes to save you when maintainer doesn't care.
date: 2023-08-02 date: 2023-08-02
authors: authors:
- key: mf - key: mf
title: a.k.a. your opinionated admin title: a.k.a. your opinionated admin
tags: tags:
- 🏭 - 🏭
- red-hat - red-hat
- copr - copr
- admin - admin
- vps - vps
--- ---
When you decide to run Fedora on your VPS, you might get screwed over by using When you decide to run Fedora on your VPS, you might get screwed over by using
@ -37,10 +37,12 @@ If you have ever used Ubuntu, you might be familiar with the concept since it is
very close to [PPAs](https://help.ubuntu.com/community/PPA). very close to [PPAs](https://help.ubuntu.com/community/PPA).
tl;dr of the whole process consists of tl;dr of the whole process consists of
1. enabling the Copr repository, and 1. enabling the Copr repository, and
2. installing the desired package. 2. installing the desired package.
So in shell you would do So in shell you would do
``` ```
# dnf copr enable copr-repository # dnf copr enable copr-repository
# dnf install package-from-the-repository # dnf install package-from-the-repository
@ -110,6 +112,7 @@ Copr is heavily used for testing builds on the upstream with
I have described above), if need be. I have described above), if need be.
[^1]: [vpsFree.cz](https://vpsfree.cz) [^1]: [vpsFree.cz](https://vpsfree.cz)
[^2]: Even though I've been running archLinux on some Raspberry Pi's and also [^2]:
Even though I've been running archLinux on some Raspberry Pi's and also
on one of my “home servers”, before getting the VPS. You could say I like on one of my “home servers”, before getting the VPS. You could say I like
to live on the edge… to live on the edge…

View file

@ -5,9 +5,9 @@ date: 2022-12-14T21:45
slug: aoc-2022/intro slug: aoc-2022/intro
authors: mf authors: mf
tags: tags:
- advent-of-code - advent-of-code
- advent-of-code-2022 - advent-of-code-2022
- rust - rust
hide_table_of_contents: false hide_table_of_contents: false
--- ---
@ -58,6 +58,7 @@ Since we are using Rust, we are going to use a [Cargo] and more than likely VSCo
with [`rust-analyzer`]. Because of my choice of libraries, we will also introduce with [`rust-analyzer`]. Because of my choice of libraries, we will also introduce
a `.envrc` file that can be used by [`direnv`], which allows you to set specific a `.envrc` file that can be used by [`direnv`], which allows you to set specific
environment variables when you enter a directory. In our case, we will use environment variables when you enter a directory. In our case, we will use
```bash ```bash
# to show nice backtrace when using the color-eyre # to show nice backtrace when using the color-eyre
export RUST_BACKTRACE=1 export RUST_BACKTRACE=1
@ -69,6 +70,7 @@ export RUST_LOG=trace
And for the one of the most obnoxious things ever, we will use a script to download And for the one of the most obnoxious things ever, we will use a script to download
the inputs instead of “_clicking, opening and copying to a file_”[^1]. There is the inputs instead of “_clicking, opening and copying to a file_”[^1]. There is
no need to be _fancy_, so we will adjust Python script by Martin[^2]. no need to be _fancy_, so we will adjust Python script by Martin[^2].
```py ```py
#!/usr/bin/env python3 #!/usr/bin/env python3
@ -116,6 +118,7 @@ if __name__ == "__main__":
If the script is called without any arguments, it will deduce the day from the If the script is called without any arguments, it will deduce the day from the
system, so we do not need to change the day every morning. It also requires a system, so we do not need to change the day every morning. It also requires a
configuration file: configuration file:
```yaml ```yaml
# env.yaml # env.yaml
session: your session cookie session: your session cookie
@ -159,6 +162,7 @@ parsing and one for 2D vector (that gets used quite often during Advent of Code)
Key part is, of course, processing the input and my library exports following Key part is, of course, processing the input and my library exports following
functions that get used a lot: functions that get used a lot:
```rust ```rust
/// Reads file to the string. /// Reads file to the string.
pub fn file_to_string<P: AsRef<Path>>(pathname: P) -> String; pub fn file_to_string<P: AsRef<Path>>(pathname: P) -> String;
@ -195,6 +199,7 @@ need.
We can also prepare a template to quickly bootstrap each of the days. We know We can also prepare a template to quickly bootstrap each of the days. We know
that each puzzle has 2 parts, which means that we can start with 2 functions that that each puzzle has 2 parts, which means that we can start with 2 functions that
will solve them. will solve them.
```rust ```rust
fn part1(input: &Input) -> Output { fn part1(input: &Input) -> Output {
todo!() todo!()
@ -210,6 +215,7 @@ of puzzles, it is the same type). `todo!()` can be used as a nice placeholder,
it also causes a panic when reached and we could also provide some string with it also causes a panic when reached and we could also provide some string with
an explanation, e.g. `todo!("part 1")`. We have not given functions a specific an explanation, e.g. `todo!("part 1")`. We have not given functions a specific
type and to avoid as much copy-paste as possible, we will introduce type aliases. type and to avoid as much copy-paste as possible, we will introduce type aliases.
```rust ```rust
type Input = String; type Input = String;
type Output = i32; type Output = i32;
@ -226,6 +232,7 @@ For each day we get a personalized input that is provided as a text file. Almost
all the time, we would like to get some structured type out of that input, and all the time, we would like to get some structured type out of that input, and
therefore it makes sense to introduce a new function that will provide the parsing therefore it makes sense to introduce a new function that will provide the parsing
of the input. of the input.
```rust ```rust
fn parse_input(path: &str) -> Input { fn parse_input(path: &str) -> Input {
todo!() todo!()
@ -237,6 +244,7 @@ sample instead of input.
OK, so now we can write a `main` function that will take all of the pieces and OK, so now we can write a `main` function that will take all of the pieces and
run them. run them.
```rust ```rust
fn main() { fn main() {
let input = parse_input("inputs/dayXX.txt"); let input = parse_input("inputs/dayXX.txt");
@ -249,6 +257,7 @@ fn main() {
This would definitely do :) But we have installed a few libraries and we want to This would definitely do :) But we have installed a few libraries and we want to
use them. In this part we are going to utilize _[`tracing`]_ (for tracing, duh…) use them. In this part we are going to utilize _[`tracing`]_ (for tracing, duh…)
and _[`color-eyre`]_ (for better error reporting, e.g. from parsing). and _[`color-eyre`]_ (for better error reporting, e.g. from parsing).
```rust ```rust
fn main() -> Result<()> { fn main() -> Result<()> {
tracing_subscriber::fmt() tracing_subscriber::fmt()
@ -274,11 +283,13 @@ The first statement will set up tracing and configure it to print out the logs t
terminal, based on the environment variable. We also change the formatting a bit, terminal, based on the environment variable. We also change the formatting a bit,
since we do not need all the _fancy_ features of the logger. Pure initialization since we do not need all the _fancy_ features of the logger. Pure initialization
would get us logs like this: would get us logs like this:
``` ```
2022-12-11T19:53:19.975343Z INFO day01: Part 1: 0 2022-12-11T19:53:19.975343Z INFO day01: Part 1: 0
``` ```
However after running that command, we will get the following: However after running that command, we will get the following:
``` ```
INFO src/bin/day01.rs:35: Part 1: 0 INFO src/bin/day01.rs:35: Part 1: 0
``` ```
@ -296,6 +307,7 @@ at the end of the `::install` which _unwraps_ the **»result«** of the installa
::: :::
Overall we will get to a template like this: Overall we will get to a template like this:
```rust ```rust
use aoc_2022::*; use aoc_2022::*;
@ -338,15 +350,18 @@ fn main() -> Result<()> {
} }
``` ```
[^1]: Copy-pasting might be a relaxing thing to do, but you can also discover [^1]:
nasty stuff about your PC. See [this Reddit post and the comment]. Copy-pasting might be a relaxing thing to do, but you can also discover
[^2]: [GitHub profile](https://github.com/martinjonas) nasty stuff about your PC. See [this Reddit post and the comment].
[^3]: Even though you can use it even for libraries, but handling errors from
libraries using `anyhow` is nasty… You will be the stinky one ;)
[_Advent of Code_]: https://adventofcode.com [^2]: [GitHub profile](https://github.com/martinjonas)
[GitLab]: https://gitlab.com/mfocko/advent-of-code-2022 [^3]:
[Cargo]: https://doc.rust-lang.org/cargo/ Even though you can use it even for libraries, but handling errors from
libraries using `anyhow` is nasty… You will be the stinky one ;)
[_advent of code_]: https://adventofcode.com
[gitlab]: https://gitlab.com/mfocko/advent-of-code-2022
[cargo]: https://doc.rust-lang.org/cargo/
[`rust-analyzer`]: https://rust-analyzer.github.io/ [`rust-analyzer`]: https://rust-analyzer.github.io/
[`direnv`]: https://direnv.net/ [`direnv`]: https://direnv.net/
[`tracing`]: https://crates.io/crates/tracing [`tracing`]: https://crates.io/crates/tracing
@ -357,4 +372,4 @@ libraries using `anyhow` is nasty… You will be the stinky one ;)
[`regex`]: https://crates.io/crates/regex [`regex`]: https://crates.io/crates/regex
[`lazy_static`]: https://crates.io/crates/lazy_static [`lazy_static`]: https://crates.io/crates/lazy_static
[`itertools`]: https://crates.io/crates/itertools [`itertools`]: https://crates.io/crates/itertools
[this Reddit post and the comment]: https://www.reddit.com/r/adventofcode/comments/zb98pn/comment/iyq0ono [this reddit post and the comment]: https://www.reddit.com/r/adventofcode/comments/zb98pn/comment/iyq0ono

View file

@ -5,9 +5,9 @@ date: 2022-12-15T01:15
slug: aoc-2022/1st-week slug: aoc-2022/1st-week
authors: mf authors: mf
tags: tags:
- advent-of-code - advent-of-code
- advent-of-code-2022 - advent-of-code-2022
- rust - rust
hide_table_of_contents: false hide_table_of_contents: false
--- ---
@ -43,6 +43,7 @@ handle samples. With each puzzle you usually get a sample input and expected
output. You can use them to verify that your solution works, or usually doesn't. output. You can use them to verify that your solution works, or usually doesn't.
At first I've decided to put asserts into my `main`, something like At first I've decided to put asserts into my `main`, something like
```rust ```rust
assert_eq!(part_1(&sample), 24000); assert_eq!(part_1(&sample), 24000);
info!("Part 1: {}", part_1(&input)); info!("Part 1: {}", part_1(&input));
@ -53,6 +54,7 @@ info!("Part 2: {}", part_2(&input));
However, once you get further, the sample input may take some time to run itself. However, once you get further, the sample input may take some time to run itself.
So in the end, I have decided to turn them into unit tests: So in the end, I have decided to turn them into unit tests:
```rust ```rust
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -124,6 +126,7 @@ Fighting the compiler took me 30 minutes.
We need to find a common item among 2 collections, that's an easy task, right? We need to find a common item among 2 collections, that's an easy task, right?
We can construct 2 sets and find an intersection: We can construct 2 sets and find an intersection:
```rust ```rust
let top: HashSet<i32> = [1, 2, 3].iter().collect(); let top: HashSet<i32> = [1, 2, 3].iter().collect();
let bottom: HashSet<i32> = [3, 4, 5].iter().collect(); let bottom: HashSet<i32> = [3, 4, 5].iter().collect();
@ -133,6 +136,7 @@ Now, the first issue that we encounter is caused by the fact that we are using
a slice (the `[…]`), iterator of that returns **references** to the numbers. a slice (the `[…]`), iterator of that returns **references** to the numbers.
And we get immediately yelled at by the compiler, because the numbers are discarded And we get immediately yelled at by the compiler, because the numbers are discarded
after running the `.collect`. To fix this, we can use `.into_iter`: after running the `.collect`. To fix this, we can use `.into_iter`:
```rust ```rust
let top: HashSet<i32> = [1, 2, 3].into_iter().collect(); let top: HashSet<i32> = [1, 2, 3].into_iter().collect();
let bottom: HashSet<i32> = [3, 4, 5].into_iter().collect(); let bottom: HashSet<i32> = [3, 4, 5].into_iter().collect();
@ -140,9 +144,11 @@ let bottom: HashSet<i32> = [3, 4, 5].into_iter().collect();
This way the numbers will get copied instead of referenced. OK, let's find the This way the numbers will get copied instead of referenced. OK, let's find the
intersection of those 2 collections: intersection of those 2 collections:
```rust ```rust
println!("Common elements: {:?}", top.intersection(&bottom)); println!("Common elements: {:?}", top.intersection(&bottom));
``` ```
``` ```
Common elements: [3] Common elements: [3]
``` ```
@ -161,6 +167,7 @@ that should be fairly easy, we have an intersection and we want to find intersec
over all of them. over all of them.
Let's have a look at the type of the `.intersection` Let's have a look at the type of the `.intersection`
```rust ```rust
pub fn intersection<'a>( pub fn intersection<'a>(
    &'a self,     &'a self,
@ -169,11 +176,13 @@ pub fn intersection<'a>(
``` ```
OK… Huh… But we have an example there! OK… Huh… But we have an example there!
```rust ```rust
let intersection: HashSet<_> = a.intersection(&b).collect(); let intersection: HashSet<_> = a.intersection(&b).collect();
``` ```
Cool, that's all we need. Cool, that's all we need.
```rust ```rust
let top: HashSet<i32> = [1, 2, 3, 4].into_iter().collect(); let top: HashSet<i32> = [1, 2, 3, 4].into_iter().collect();
let bottom: HashSet<i32> = [3, 4, 5, 6].into_iter().collect(); let bottom: HashSet<i32> = [3, 4, 5, 6].into_iter().collect();
@ -183,11 +192,13 @@ let bottom_2: HashSet<i32> = [4, 5, 6].into_iter().collect();
let intersection: HashSet<_> = top.intersection(&bottom).collect(); let intersection: HashSet<_> = top.intersection(&bottom).collect();
println!("Intersection: {:?}", intersection); println!("Intersection: {:?}", intersection);
``` ```
``` ```
Intersection: {3, 4} Intersection: {3, 4}
``` ```
Cool, so let's do the intersection with the `top_2`: Cool, so let's do the intersection with the `top_2`:
```rust ```rust
let top: HashSet<i32> = [1, 2, 3, 4].into_iter().collect(); let top: HashSet<i32> = [1, 2, 3, 4].into_iter().collect();
let bottom: HashSet<i32> = [3, 4, 5, 6].into_iter().collect(); let bottom: HashSet<i32> = [3, 4, 5, 6].into_iter().collect();
@ -200,6 +211,7 @@ println!("Intersection: {:?}", intersection);
``` ```
And we get yelled at by the compiler: And we get yelled at by the compiler:
``` ```
error[E0308]: mismatched types error[E0308]: mismatched types
--> src/main.rs:10:58 --> src/main.rs:10:58
@ -228,6 +240,7 @@ making sure you're not doing something naughty that may cause an **undefined**
::: :::
To resolve this we need to get an iterator that **clones** the elements: To resolve this we need to get an iterator that **clones** the elements:
```rust ```rust
let top: HashSet<i32> = [1, 2, 3, 4].into_iter().collect(); let top: HashSet<i32> = [1, 2, 3, 4].into_iter().collect();
let bottom: HashSet<i32> = [3, 4, 5, 6].into_iter().collect(); let bottom: HashSet<i32> = [3, 4, 5, 6].into_iter().collect();
@ -239,6 +252,7 @@ let intersection: HashSet<_> = intersection.intersection(&top_2).cloned().collec
let intersection: HashSet<_> = intersection.intersection(&bottom_2).cloned().collect(); let intersection: HashSet<_> = intersection.intersection(&bottom_2).cloned().collect();
println!("Intersection: {:?}", intersection); println!("Intersection: {:?}", intersection);
``` ```
``` ```
Intersection: {4} Intersection: {4}
``` ```
@ -273,6 +287,7 @@ Let's play with stacks of crates.
::: :::
Very easy problem with very annoying input. You can judge yourself: Very easy problem with very annoying input. You can judge yourself:
``` ```
[D] [D]
[N] [C] [N] [C]
@ -287,7 +302,6 @@ move 1 from 1 to 2
Good luck transforming that into something reasonable :) Good luck transforming that into something reasonable :)
:::tip Fun fact :::tip Fun fact
Took me 40 minutes to parse this reasonably, including fighting the compiler. Took me 40 minutes to parse this reasonably, including fighting the compiler.
@ -300,6 +314,7 @@ For the initial solution I went with a manual solution (as in _I have done all_
_the work_. Later on I have decided to explore the `std` and interface of the _the work_. Later on I have decided to explore the `std` and interface of the
`std::vec::Vec` and found [`split_off`] which takes an index and splits (duh) `std::vec::Vec` and found [`split_off`] which takes an index and splits (duh)
the vector: the vector:
```rust ```rust
let mut vec = vec![1, 2, 3]; let mut vec = vec![1, 2, 3];
let vec2 = vec.split_off(1); let vec2 = vec.split_off(1);
@ -343,11 +358,12 @@ directories that take a lot of space and should be deleted.
::: :::
> I was waiting for this moment, and yet it got me! > I was waiting for this moment, and yet it got me!
> *imagine me swearing for hours* > _imagine me swearing for hours_
### Solution ### Solution
We need to “_build_” a file system from the input that is given in a following form: We need to “_build_” a file system from the input that is given in a following form:
``` ```
$ cd / $ cd /
$ ls $ ls
@ -417,6 +433,7 @@ to have `Rc<RefCell<T>>`.
So, how are we going to represent the file system then? We will use an enumeration, So, how are we going to represent the file system then? We will use an enumeration,
hehe, which is an algebraic data type that can store some stuff in itself :weary: hehe, which is an algebraic data type that can store some stuff in itself :weary:
```rust ```rust
type FileHandle = Rc<RefCell<AocFile>>; type FileHandle = Rc<RefCell<AocFile>>;
@ -433,6 +450,7 @@ out the value of that enumeration, it's derived, so it's not as good as if we ha
implemented it ourselves, but it's good enough for debugging, hence the name. implemented it ourselves, but it's good enough for debugging, hence the name.
Now to the fun part! `AocFile` value can be represented in two ways: Now to the fun part! `AocFile` value can be represented in two ways:
- `File(usize)`, e.g. `AocFile::File(123)` and we can pattern match it, if we - `File(usize)`, e.g. `AocFile::File(123)` and we can pattern match it, if we
need to need to
- `Directory(BTreeMap<String, FileHandle>)` will represent the directory and will - `Directory(BTreeMap<String, FileHandle>)` will represent the directory and will
@ -491,13 +509,13 @@ in Rust, we can say that
You can easily see that they only differ in the mutability. (And even that is not You can easily see that they only differ in the mutability. (And even that is not
as simple as it seems, because there is also `Cell<T>`) as simple as it seems, because there is also `Cell<T>`)
[_Advent of Code_]: https://adventofcode.com [_advent of code_]: https://adventofcode.com
[GitLab]: https://gitlab.com/mfocko/advent-of-code-2022 [gitlab]: https://gitlab.com/mfocko/advent-of-code-2022
[`/src/bin/`]: https://gitlab.com/mfocko/advent-of-code-2022/-/tree/main/src/bin [`/src/bin/`]: https://gitlab.com/mfocko/advent-of-code-2022/-/tree/main/src/bin
[`sccache`]: https://github.com/mozilla/sccache [`sccache`]: https://github.com/mozilla/sccache
[`RangeInclusive`]: https://doc.rust-lang.org/std/ops/struct.RangeInclusive.html [`rangeinclusive`]: https://doc.rust-lang.org/std/ops/struct.RangeInclusive.html
[`split_off`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.split_off [`split_off`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.split_off
[`du`]: https://www.man7.org/linux/man-pages/man1/du.1.html [`du`]: https://www.man7.org/linux/man-pages/man1/du.1.html
[`HashMap`]: https://doc.rust-lang.org/std/collections/struct.HashMap.html [`hashmap`]: https://doc.rust-lang.org/std/collections/struct.HashMap.html
[`BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html [`btreemap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
[_tree catamorphism_]: https://en.wikipedia.org/wiki/Catamorphism#Tree_fold [_tree catamorphism_]: https://en.wikipedia.org/wiki/Catamorphism#Tree_fold

View file

@ -5,9 +5,9 @@ date: 2022-12-25T23:15
slug: aoc-2022/2nd-week slug: aoc-2022/2nd-week
authors: mf authors: mf
tags: tags:
- advent-of-code - advent-of-code
- advent-of-code-2022 - advent-of-code-2022
- rust - rust
hide_table_of_contents: false hide_table_of_contents: false
--- ---
@ -109,6 +109,7 @@ break in multiple places at once. I'll get back to it…
::: :::
Let's split it in multiple parts: Let's split it in multiple parts:
- `v: &'a [Vec<U>]` represents the 2D `Vec`, we are indexing, `Vec` implements - `v: &'a [Vec<U>]` represents the 2D `Vec`, we are indexing, `Vec` implements
`Slice` trait and _clippy_ recommends using `&[T]` to `&Vec<T>`, exact details `Slice` trait and _clippy_ recommends using `&[T]` to `&Vec<T>`, exact details
are unknown to me are unknown to me
@ -128,6 +129,7 @@ Let's split it in multiple parts:
First issue that our implementation has is the fact that we cannot get a mutable First issue that our implementation has is the fact that we cannot get a mutable
reference out of that function. This could be easily resolved by introducing new reference out of that function. This could be easily resolved by introducing new
function, e.g. `index_mut`. Which I have actually done while writing this part: function, e.g. `index_mut`. Which I have actually done while writing this part:
```rust ```rust
pub fn index_mut<'a, T, U>(v: &'a mut [Vec<U>], idx: &Vector2D<T>) -> &'a mut U pub fn index_mut<'a, T, U>(v: &'a mut [Vec<U>], idx: &Vector2D<T>) -> &'a mut U
where where
@ -153,6 +155,7 @@ types for indexing “built-in” types.
Another part of this rabbit hole is trait `SliceIndex<T>` that is of a relevance Another part of this rabbit hole is trait `SliceIndex<T>` that is of a relevance
because of because of
```rust ```rust
impl<T, I> Index<I> for [T] impl<T, I> Index<I> for [T]
where where
@ -177,6 +180,7 @@ and is marked as `unsafe`.
Another problem is a requirement for indexing either `[Vec<T>]` or `Vec<Vec<T>>`. Another problem is a requirement for indexing either `[Vec<T>]` or `Vec<Vec<T>>`.
This requirement could be countered by removing inner type `Vec<T>` and constraining This requirement could be countered by removing inner type `Vec<T>` and constraining
it by a trait `Index` (or `IndexMut` respectively) in a following way it by a trait `Index` (or `IndexMut` respectively) in a following way
```rust ```rust
pub fn index<'a, C, T>(v: &'a [C], idx: &Vector2D<T>) -> &'a C::Output pub fn index<'a, C, T>(v: &'a [C], idx: &Vector2D<T>) -> &'a C::Output
where where
@ -203,6 +207,7 @@ that you can use to your advantage; you can easily guess how).
So how can we approach this then? Well… we will convert the bounds instead of So how can we approach this then? Well… we will convert the bounds instead of
the indices and that lead us to: the indices and that lead us to:
```rust ```rust
pub fn in_range<T, U>(v: &[Vec<U>], idx: &Vector2D<T>) -> bool pub fn in_range<T, U>(v: &[Vec<U>], idx: &Vector2D<T>) -> bool
where where
@ -225,6 +230,7 @@ You can tell that it's definitely a shitty code. Let's improve it now! We will
get back to the original idea, but do it better. We know that we cannot convert get back to the original idea, but do it better. We know that we cannot convert
negative values into `usize`, **but** we also know that conversion like that negative values into `usize`, **but** we also know that conversion like that
returns a `Result<T, E>` which we can use to our advantage. returns a `Result<T, E>` which we can use to our advantage.
```rust ```rust
pub fn in_range<T, U>(v: &[Vec<U>], idx: &Vector2D<T>) -> bool pub fn in_range<T, U>(v: &[Vec<U>], idx: &Vector2D<T>) -> bool
where where
@ -247,6 +253,7 @@ method returns `Result<T, E>`.
We call `and_then` on that _result_, let's have a look at the type signature of We call `and_then` on that _result_, let's have a look at the type signature of
`and_then`, IMO it explains more than enough: `and_then`, IMO it explains more than enough:
```rust ```rust
pub fn and_then<U, F>(self, op: F) -> Result<U, E> pub fn and_then<U, F>(self, op: F) -> Result<U, E>
where where
@ -297,6 +304,7 @@ generates a lot of boilerplate. And even though it can be easily copied, it's
just a waste of space and unnecessary code. Let's “simplify” this (on one end just a waste of space and unnecessary code. Let's “simplify” this (on one end
while creating monster on the other end). I've gone through what we need in the while creating monster on the other end). I've gone through what we need in the
preparations for the AoC. Let's sum up our requirements: preparations for the AoC. Let's sum up our requirements:
- parsing - parsing
- part 1 & 2 - part 1 & 2
- running on sample / input - running on sample / input
@ -307,6 +315,7 @@ cannot do anything about it. However running and testing can be simplified!
Let's introduce and export a new module `solution` that will take care of all of Let's introduce and export a new module `solution` that will take care of all of
this. We will start by introducing a trait for each day. this. We will start by introducing a trait for each day.
```rust ```rust
pub trait Solution<Input, Output: Display> { pub trait Solution<Input, Output: Display> {
fn parse_input<P: AsRef<Path>>(pathname: P) -> Input; fn parse_input<P: AsRef<Path>>(pathname: P) -> Input;
@ -322,6 +331,7 @@ implement the `Solution` trait.
Now we need to get rid of the boilerplate, we can't get rid of the `main` function, Now we need to get rid of the boilerplate, we can't get rid of the `main` function,
but we can at least move out the functionality. but we can at least move out the functionality.
```rust ```rust
fn run(type_of_input: &str) -> Result<()> fn run(type_of_input: &str) -> Result<()>
where where
@ -382,6 +392,7 @@ advised to use it any production code.
And now we can get to the nastiest stuff :weary: We will **generate** the tests! And now we can get to the nastiest stuff :weary: We will **generate** the tests!
We want to be able to generate tests for sample input in a following way: We want to be able to generate tests for sample input in a following way:
```rust ```rust
test_sample!(day_01, Day01, 42, 69); test_sample!(day_01, Day01, 42, 69);
``` ```
@ -420,6 +431,7 @@ parameters have their name prefixed with `$` sign and you can define various “
of your macro. Let's go through it! of your macro. Let's go through it!
We have following parameters: We have following parameters:
- `$mod_name` which represents the name for the module with tests, it is typed - `$mod_name` which represents the name for the module with tests, it is typed
with `ident` which means that we want a valid identifier to be passed in. with `ident` which means that we want a valid identifier to be passed in.
- `$day_struct` represents the structure that will be used for tests, it is typed - `$day_struct` represents the structure that will be used for tests, it is typed
@ -429,6 +441,7 @@ We have following parameters:
Apart from that we need to use `#[macro_export]` to mark the macro as exported Apart from that we need to use `#[macro_export]` to mark the macro as exported
for usage outside of the module. Now our skeleton looks like: for usage outside of the module. Now our skeleton looks like:
```rust ```rust
use aoc_2022::*; use aoc_2022::*;
@ -476,6 +489,7 @@ And the issue is caused by different types of `Output` for the part 1 and part 2
Problem is relatively simple and consists of simulating a CPU, I have approached Problem is relatively simple and consists of simulating a CPU, I have approached
it in a following way: it in a following way:
```rust ```rust
fn evaluate_instructions(instructions: &[Instruction], mut out: Output) -> Output { fn evaluate_instructions(instructions: &[Instruction], mut out: Output) -> Output {
instructions instructions
@ -500,6 +514,7 @@ have an `enumeration` that can _bear_ some other values apart from the type itse
We could've seen something like this with the `Result<T, E>` type that can be We could've seen something like this with the `Result<T, E>` type that can be
defined as defined as
```rust ```rust
enum Result<T, E> { enum Result<T, E> {
Ok(T), Ok(T),
@ -512,6 +527,7 @@ enum Result<T, E> {
When we have an `Ok` value, it has the result itself, and when we get an `Err` When we have an `Ok` value, it has the result itself, and when we get an `Err`
value, it has the error. This also allows us to handle _results_ in a rather value, it has the error. This also allows us to handle _results_ in a rather
pretty way: pretty way:
```rust ```rust
match do_something(x) { match do_something(x) {
Ok(y) => { Ok(y) => {
@ -526,6 +542,7 @@ match do_something(x) {
::: :::
My solution has a following outline: My solution has a following outline:
```rust ```rust
fn execute(&self, i: &Instruction, output: &mut Output) -> State { fn execute(&self, i: &Instruction, output: &mut Output) -> State {
// execute the instruction // execute the instruction
@ -586,6 +603,7 @@ also rolling down the hill…
As I have said in the _tl;dr_, we are looking for the shortest path, but the start As I have said in the _tl;dr_, we are looking for the shortest path, but the start
and goal differ for the part 1 and 2. So I have decided to refactor my solution and goal differ for the part 1 and 2. So I have decided to refactor my solution
to a BFS algorithm that takes necessary parameters via functions: to a BFS algorithm that takes necessary parameters via functions:
```rust ```rust
fn bfs<F, G>( fn bfs<F, G>(
graph: &[Vec<char>], start: &Position, has_edge: F, is_target: G graph: &[Vec<char>], start: &Position, has_edge: F, is_target: G
@ -621,6 +639,7 @@ Processing packets with structured data from the distress signal.
You can implement a lot of traits if you want to. It is _imperative_ to implement You can implement a lot of traits if you want to. It is _imperative_ to implement
ordering on the packets. I had a typo, so I also proceeded to implement a `Display` ordering on the packets. I had a typo, so I also proceeded to implement a `Display`
trait for debugging purposes: trait for debugging purposes:
```rust ```rust
impl Display for Packet { impl Display for Packet {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@ -765,13 +784,14 @@ Why? We have it implemented for the slices (`[C]`), why doesn't it work? Well,
the fun part consists of the fact that in other place, where we were using it, the fun part consists of the fact that in other place, where we were using it,
we were passing the `&[Vec<T>]`, but this is coming from a helper functions that we were passing the `&[Vec<T>]`, but this is coming from a helper functions that
take `&Vec<Vec<T>>` instead. And… we don't implement `Index` and `IndexMut` for take `&Vec<Vec<T>>` instead. And… we don't implement `Index` and `IndexMut` for
those. Just for the slices. :exploding_head: *What are we going to do about it?* those. Just for the slices. 🤯 _What are we going to do about it?_
We can either start copy-pasting or be smarter about it… I choose to be smarter, We can either start copy-pasting or be smarter about it… I choose to be smarter,
so let's implement a macro! The only difference across the implementations are so let's implement a macro! The only difference across the implementations are
the types of the outer containers. Implementation doesn't differ **at all**! the types of the outer containers. Implementation doesn't differ **at all**!
Implementing the macro can be done in a following way: Implementing the macro can be done in a following way:
```rust ```rust
macro_rules! generate_indices { macro_rules! generate_indices {
($container:ty) => { ($container:ty) => {
@ -807,6 +827,7 @@ macro_rules! generate_indices {
``` ```
And now we can simply do And now we can simply do
```rust ```rust
generate_indices!(VecDeque<C>); generate_indices!(VecDeque<C>);
generate_indices!([C]); generate_indices!([C]);
@ -830,6 +851,7 @@ copy-paste, cause the cost of this “monstrosity” outweighs the benefits of n
This issue is relatively funny. If you don't use any type aliases, just the raw This issue is relatively funny. If you don't use any type aliases, just the raw
types, you'll get suggested certain changes by the _clippy_. For example if you types, you'll get suggested certain changes by the _clippy_. For example if you
consider the following piece of code consider the following piece of code
```rust ```rust
fn get_sum(nums: &Vec<i32>) -> i32 { fn get_sum(nums: &Vec<i32>) -> i32 {
nums.iter().sum() nums.iter().sum()
@ -842,6 +864,7 @@ fn main() {
``` ```
and you run _clippy_ on it, you will get and you run _clippy_ on it, you will get
``` ```
Checking playground v0.0.1 (/playground) Checking playground v0.0.1 (/playground)
warning: writing `&Vec` instead of `&[_]` involves a new object where a slice will do warning: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
@ -858,6 +881,7 @@ warning: `playground` (bin "playground") generated 1 warning
``` ```
However, if you introduce a type alias, such as However, if you introduce a type alias, such as
```rust ```rust
type Numbers = Vec<i32>; type Numbers = Vec<i32>;
``` ```
@ -865,5 +889,5 @@ type Numbers = Vec<i32>;
Then _clippy_ won't say anything, cause there is literally nothing to suggest. Then _clippy_ won't say anything, cause there is literally nothing to suggest.
However the outcome is not the same… However the outcome is not the same…
[_Advent of Code_]: https://adventofcode.com [_advent of code_]: https://adventofcode.com
[BFS above]: #day-12-hill-climbing-algorithm [bfs above]: #day-12-hill-climbing-algorithm

View file

@ -5,9 +5,9 @@ date: 2023-07-06T21:00
slug: aoc-2022/3rd-week slug: aoc-2022/3rd-week
authors: mf authors: mf
tags: tags:
- advent-of-code - advent-of-code
- advent-of-code-2022 - advent-of-code-2022
- rust - rust
hide_table_of_contents: false hide_table_of_contents: false
--- ---
@ -51,6 +51,7 @@ underlying data structure.
Here you can see a rather short snippet from the solution that allows you to Here you can see a rather short snippet from the solution that allows you to
“index” the graph: “index” the graph:
```rust ```rust
impl Index<&str> for Graph { impl Index<&str> for Graph {
type Output = Vertex; type Output = Vertex;
@ -78,6 +79,7 @@ different ways the work can be split.
Being affected by _functional programming brain damage_:tm:, I have chosen to Being affected by _functional programming brain damage_:tm:, I have chosen to
do this part by function that returns an iterator over the possible ways: do this part by function that returns an iterator over the possible ways:
```rust ```rust
fn pairings( fn pairings(
valves: &BTreeSet<String>, valves: &BTreeSet<String>,
@ -123,6 +125,7 @@ iterate through the positions that can actually collide with the wall or other
piece. piece.
To get the desired behaviour, you can just compose few smaller functions: To get the desired behaviour, you can just compose few smaller functions:
```rust ```rust
fn occupied(shape: &[Vec<char>]) -> impl Iterator<Item = Position> + '_ { fn occupied(shape: &[Vec<char>]) -> impl Iterator<Item = Position> + '_ {
shape.iter().enumerate().flat_map(|(y, row)| { shape.iter().enumerate().flat_map(|(y, row)| {
@ -140,12 +143,12 @@ fn occupied(shape: &[Vec<char>]) -> impl Iterator<Item = Position> + '_ {
In the end, we get relative positions which we can adjust later when given the In the end, we get relative positions which we can adjust later when given the
specific positions from iterator. You can see some interesting parts in this: specific positions from iterator. You can see some interesting parts in this:
* `.enumerate()` allows us to get both the indices (coordinates) and the line - `.enumerate()` allows us to get both the indices (coordinates) and the line
or, later on, the character itself, or, later on, the character itself,
* `.flat_map()` flattens the iterator, i.e. when we return another iterator, - `.flat_map()` flattens the iterator, i.e. when we return another iterator,
they just get chained instead of iterating over iterators (which sounds pretty they just get chained instead of iterating over iterators (which sounds pretty
disturbing, doesn't it?), disturbing, doesn't it?),
* and finally `.filter_map()` which is pretty similar to the “basic” `.map()` - and finally `.filter_map()` which is pretty similar to the “basic” `.map()`
with a one, key, difference that it expects the items of an iterator to be with a one, key, difference that it expects the items of an iterator to be
mapped to an `Option<T>` from which it ignores nothing (as in `None` :wink:) mapped to an `Option<T>` from which it ignores nothing (as in `None` :wink:)
and also unwraps the values from `Some(…)`. and also unwraps the values from `Some(…)`.
@ -156,6 +159,7 @@ In the solution we cycle through both Tetris-like shapes that fall down and the
jets that move our pieces around. Initially I have implemented my own infinite jets that move our pieces around. Initially I have implemented my own infinite
iterator that just yields the indices. It is a very simple, yet powerful, piece iterator that just yields the indices. It is a very simple, yet powerful, piece
of code: of code:
```rust ```rust
struct InfiniteIndex { struct InfiniteIndex {
size: usize, size: usize,
@ -253,6 +257,7 @@ a rather interesting issue with `.borrow_mut()` method being used on `Rc<RefCell
#### `.borrow_mut()` #### `.borrow_mut()`
Consider the following snippet of the code (taken from the documentation): Consider the following snippet of the code (taken from the documentation):
```rust ```rust
use std::cell::{RefCell, RefMut}; use std::cell::{RefCell, RefMut};
use std::collections::HashMap; use std::collections::HashMap;
@ -289,6 +294,7 @@ It is a very primitive example for `Rc<RefCell<T>>` and mutable borrow.
If you uncomment the 4th line with `use std::borrow::BorrowMut;`, you cannot If you uncomment the 4th line with `use std::borrow::BorrowMut;`, you cannot
compile the code anymore, because of compile the code anymore, because of
``` ```
Compiling playground v0.0.1 (/playground) Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types error[E0308]: mismatched types
@ -349,8 +355,9 @@ method. OK, but how can we call it on the `Rc<T>`? Easily! `Rc<T>` implements
`T` objects. If we read on _`Deref` coercion_, we can see the following: `T` objects. If we read on _`Deref` coercion_, we can see the following:
> If `T` implements `Deref<Target = U>`, …: > If `T` implements `Deref<Target = U>`, …:
> * … >
> * `T` implicitly implements all the (immutable) methods of the type `U`. > - …
> - `T` implicitly implements all the (immutable) methods of the type `U`.
What is the requirement for the `.borrow_mut()` on `RefCell<T>`? Well, it needs What is the requirement for the `.borrow_mut()` on `RefCell<T>`? Well, it needs
`&self`, so the `Deref` implements the `.borrow_mut()` for the `Rc<RefCell<T>>`. `&self`, so the `Deref` implements the `.borrow_mut()` for the `Rc<RefCell<T>>`.
@ -360,6 +367,7 @@ What is the requirement for the `.borrow_mut()` on `RefCell<T>`? Well, it needs
I have not been able to find a lot on this trait. My guess is that it provides a I have not been able to find a lot on this trait. My guess is that it provides a
method instead of a syntactic sugar (`&mut x`) for the mutable borrow. And also method instead of a syntactic sugar (`&mut x`) for the mutable borrow. And also
it provides default implementations for the types: it provides default implementations for the types:
```rust ```rust
impl BorrowMut<str> for String impl BorrowMut<str> for String
@ -390,6 +398,7 @@ Now the question is why did it break the code… My first take was that the type
the `use` overrides it with the default, which is true **in a sense**. However the `use` overrides it with the default, which is true **in a sense**. However
there is no _specialized_ implementation. Let's have a look at the trait and the there is no _specialized_ implementation. Let's have a look at the trait and the
type signature on the `RefCell<T>`: type signature on the `RefCell<T>`:
```rust ```rust
// trait // trait
pub trait BorrowMut<Borrowed>: Borrow<Borrowed> pub trait BorrowMut<Borrowed>: Borrow<Borrowed>
@ -474,6 +483,6 @@ left and right :smile:
[^2]: Pardon my example from the graph algorithms ;) [^2]: Pardon my example from the graph algorithms ;)
[^3]: [`Neg`](https://doc.rust-lang.org/std/ops/trait.Neg.html) docs [^3]: [`Neg`](https://doc.rust-lang.org/std/ops/trait.Neg.html) docs
[_Advent of Code_]: https://adventofcode.com [_advent of code_]: https://adventofcode.com
[`itertools`]: https://crates.io/crates/itertools [`itertools`]: https://crates.io/crates/itertools
[this Reddit post and the comment]: https://www.reddit.com/r/adventofcode/comments/zb98pn/comment/iyq0ono [this reddit post and the comment]: https://www.reddit.com/r/adventofcode/comments/zb98pn/comment/iyq0ono

View file

@ -5,9 +5,9 @@ date: 2023-07-07T15:14
slug: aoc-2022/4th-week slug: aoc-2022/4th-week
authors: mf authors: mf
tags: tags:
- advent-of-code - advent-of-code
- advent-of-code-2022 - advent-of-code-2022
- rust - rust
hide_table_of_contents: false hide_table_of_contents: false
--- ---
@ -46,6 +46,7 @@ rows (cause each row is a `Vec` element), but not for the columns, since they
span multiple rows. span multiple rows.
For this use case I have implemented my own _column iterator_: For this use case I have implemented my own _column iterator_:
```rust ```rust
pub struct ColumnIterator<'a, T> { pub struct ColumnIterator<'a, T> {
map: &'a [Vec<T>], map: &'a [Vec<T>],
@ -76,6 +77,7 @@ impl<'a, T> Iterator for ColumnIterator<'a, T> {
Given this piece of an iterator, it is very easy to factor out the common Given this piece of an iterator, it is very easy to factor out the common
functionality between the rows and columns into: functionality between the rows and columns into:
```rust ```rust
let mut find_boundaries = |constructor: fn(usize) -> Orientation, let mut find_boundaries = |constructor: fn(usize) -> Orientation,
iterator: &mut dyn Iterator<Item = &char>, iterator: &mut dyn Iterator<Item = &char>,
@ -92,6 +94,7 @@ let mut find_boundaries = |constructor: fn(usize) -> Orientation,
``` ```
And then use it as such: And then use it as such:
```rust ```rust
// construct all horizontal boundaries // construct all horizontal boundaries
(0..map.len()).for_each(|row| { (0..map.len()).for_each(|row| {
@ -119,6 +122,7 @@ And then use it as such:
Once the 2nd part got introduced, you start to think about a way how not to Once the 2nd part got introduced, you start to think about a way how not to
copy-paste a lot of stuff (I haven't avoided it anyways…). In this problem, I've copy-paste a lot of stuff (I haven't avoided it anyways…). In this problem, I've
chosen to introduce a trait (i.e. _interface_) for 2D and 3D walker. chosen to introduce a trait (i.e. _interface_) for 2D and 3D walker.
```rust ```rust
trait Wrap: Clone { trait Wrap: Clone {
type State; type State;
@ -139,14 +143,16 @@ trait Wrap: Clone {
Each walker maintains its own state and also provides the functions that are Each walker maintains its own state and also provides the functions that are
used during the simulation. The “promised” methods are separated into: used during the simulation. The “promised” methods are separated into:
* _simulation_-related: that are used during the simulation from the `.fold()`
* _movement_-related: just a one method that holds most of the logic differences - _simulation_-related: that are used during the simulation from the `.fold()`
- _movement_-related: just a one method that holds most of the logic differences
between 2D and 3D between 2D and 3D
* _final answer_: which extracts the _proof of solution_ from the - _final answer_: which extracts the _proof of solution_ from the
implementation-specific walker implementation-specific walker
Both 2D and 3D versions borrow the original input and therefore you must Both 2D and 3D versions borrow the original input and therefore you must
annotate the lifetime of it: annotate the lifetime of it:
```rust ```rust
struct Wrap2D<'a> { struct Wrap2D<'a> {
input: &'a Input, input: &'a Input,
@ -179,6 +185,7 @@ When writing the parsing for this problem, the first thing I have spotted on the
`char` was the `.is_digit()` function that takes a radix as a parameter. Clippy `char` was the `.is_digit()` function that takes a radix as a parameter. Clippy
noticed that I use `radix = 10` and suggested switching to `.is_ascii_digit()` noticed that I use `radix = 10` and suggested switching to `.is_ascii_digit()`
that does exactly the same thing: that does exactly the same thing:
```diff ```diff
- .take_while(|c| c.is_digit(10)) - .take_while(|c| c.is_digit(10))
+ .take_while(|c| c.is_ascii_digit()) + .take_while(|c| c.is_ascii_digit())
@ -189,6 +196,7 @@ to get the $n$-th element from it. You know the `.skip()`, you know the
`.next()`, just “slap” them together and we're done for :grin: Well, I got `.next()`, just “slap” them together and we're done for :grin: Well, I got
suggested to use `.nth()` that does exactly the combination of the two mentioned suggested to use `.nth()` that does exactly the combination of the two mentioned
methods on iterators: methods on iterators:
```diff ```diff
- match it.clone().skip(skip).next().unwrap() { - match it.clone().skip(skip).next().unwrap() {
+ match it.clone().nth(skip).unwrap() { + match it.clone().nth(skip).unwrap() {
@ -214,6 +222,7 @@ minimum that are, of course, exactly the same except for initial values and
comparators, it looks like a rather simple fix, but typing in Rust is something comparators, it looks like a rather simple fix, but typing in Rust is something
else, right? In the end I settled for a function that computes both boundaries else, right? In the end I settled for a function that computes both boundaries
without any duplication while using a closure: without any duplication while using a closure:
```rust ```rust
fn get_bounds(positions: &Input) -> (Vector2D<isize>, Vector2D<isize>) { fn get_bounds(positions: &Input) -> (Vector2D<isize>, Vector2D<isize>) {
let f = |init, cmp: &dyn Fn(isize, isize) -> isize| { let f = |init, cmp: &dyn Fn(isize, isize) -> isize| {
@ -234,9 +243,11 @@ bounding rectangle of all elves.
You might ask why would we need a closure and the answer is that `positions` You might ask why would we need a closure and the answer is that `positions`
cannot be captured from within the nested function, only via closure. One more cannot be captured from within the nested function, only via closure. One more
fun fact on top of that is the type of the comparator fun fact on top of that is the type of the comparator
```rust ```rust
&dyn Fn(isize, isize) -> isize &dyn Fn(isize, isize) -> isize
``` ```
Once we remove the `dyn` keyword, compiler yells at us and also includes a way Once we remove the `dyn` keyword, compiler yells at us and also includes a way
how to get a more thorough explanation of the error by running how to get a more thorough explanation of the error by running
@ -277,8 +288,9 @@ Not all of the explanations are helpful though, in some cases they might be even
more confusing than helpful, since they address _very simple_ use cases. more confusing than helpful, since they address _very simple_ use cases.
As you can see, even in this case there are two sides to the explanations: As you can see, even in this case there are two sides to the explanations:
* it explains why you need to use `dyn`, but
* it still mentions that trait objects need to be heap-allocated via `Box<T>` - it explains why you need to use `dyn`, but
- it still mentions that trait objects need to be heap-allocated via `Box<T>`
that, as you can see in my snippet, **does not** apply here :smile: IMO it's that, as you can see in my snippet, **does not** apply here :smile: IMO it's
caused by the fact that we are borrowing it and therefore we don't need to caused by the fact that we are borrowing it and therefore we don't need to
care about the size or whereabouts of it. care about the size or whereabouts of it.
@ -352,6 +364,7 @@ more verbose.
I'll skip the boring parts of checking bounds and entry/exit of the basin :wink: I'll skip the boring parts of checking bounds and entry/exit of the basin :wink:
We can easily calculate positions of the blizzards using a modular arithmetics: We can easily calculate positions of the blizzards using a modular arithmetics:
```rust ```rust
impl Index<Position> for Basin { impl Index<Position> for Basin {
type Output = char; type Output = char;
@ -428,12 +441,14 @@ that promotes vertices closer to the exit **and** with a minimum time taken.
::: :::
Cost function is, of course, a closure :wink: Cost function is, of course, a closure :wink:
```rust ```rust
let cost = |p: Position| p.z() as usize + exit.y().abs_diff(p.y()) + exit.x().abs_diff(p.x()); let cost = |p: Position| p.z() as usize + exit.y().abs_diff(p.y()) + exit.x().abs_diff(p.x());
``` ```
And also for checking the possible moves from the current vertex, I have And also for checking the possible moves from the current vertex, I have
implemented, yet another, closure that yields an iterator with the next moves: implemented, yet another, closure that yields an iterator with the next moves:
```rust ```rust
let next_positions = |p| { let next_positions = |p| {
[(0, 0, 1), (0, -1, 1), (0, 1, 1), (-1, 0, 1), (1, 0, 1)] [(0, 0, 1), (0, -1, 1), (0, 1, 1), (-1, 0, 1), (1, 0, 1)]
@ -461,6 +476,7 @@ popping the most prioritized elements yields values wrapped in the `Reverse`.
For this purpose I have just taken the max-heap and wrapped it as a whole in a For this purpose I have just taken the max-heap and wrapped it as a whole in a
separate structure providing just the desired methods: separate structure providing just the desired methods:
```rust ```rust
use std::cmp::{Ord, Reverse}; use std::cmp::{Ord, Reverse};
use std::collections::BinaryHeap; use std::collections::BinaryHeap;
@ -509,14 +525,16 @@ with a rather easy solution, as the last day always seems to be.
Implementing 2 functions, converting from the _SNAFU base_ and back to the _SNAFU_ Implementing 2 functions, converting from the _SNAFU base_ and back to the _SNAFU_
_base_ representation. Let's do a bit more though! I have implemented two functions: _base_ representation. Let's do a bit more though! I have implemented two functions:
* `from_snafu`
* `to_snafu` - `from_snafu`
- `to_snafu`
Now it is apparent that all I do is number to string and string to number. Hmm… Now it is apparent that all I do is number to string and string to number. Hmm…
that sounds familiar, doesn't it? Let's introduce a structure for the SNAFU numbers that sounds familiar, doesn't it? Let's introduce a structure for the SNAFU numbers
and implement the traits that we need. and implement the traits that we need.
Let's start with a structure: Let's start with a structure:
```rust ```rust
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
struct SNAFU { struct SNAFU {
@ -550,6 +568,7 @@ After those changes we need to adjust the code and tests.
Parsing of the input is very easy, before we have used the lines, now we parse Parsing of the input is very easy, before we have used the lines, now we parse
everything: everything:
```diff ```diff
fn parse_input<P: AsRef<Path>>(pathname: P) -> Input { fn parse_input<P: AsRef<Path>>(pathname: P) -> Input {
- file_to_lines(pathname) - file_to_lines(pathname)
@ -558,6 +577,7 @@ everything:
``` ```
Part 1 needs to be adjusted a bit too: Part 1 needs to be adjusted a bit too:
```diff ```diff
fn part_1(input: &Input) -> Output { fn part_1(input: &Input) -> Output {
- to_snafu(input.iter().map(|s| from_snafu(s)).sum()) - to_snafu(input.iter().map(|s| from_snafu(s)).sum())
@ -569,6 +589,7 @@ You can also see that it simplifies the meaning a bit and it is more explicit th
the previous versions. the previous versions.
And for the tests: And for the tests:
```diff ```diff
#[test] #[test]
fn test_from() { fn test_from() {
@ -597,7 +618,7 @@ Let's wrap the whole thing up! Keeping in mind both AoC and the Rust…
### Advent of Code ### Advent of Code
This year was quite fun, even though most of the solutions and posts came in This year was quite fun, even though most of the solutions and posts came in
later on (*cough* in '23 *cough*). Day 22 was the most obnoxious one… And also later on (_cough_ in '23 _cough_). Day 22 was the most obnoxious one… And also
it feels like I used priority queues and tree data structures **a lot** :eyes: it feels like I used priority queues and tree data structures **a lot** :eyes:
### with Rust ### with Rust
@ -634,8 +655,8 @@ tl;dr Relatively pleasant language until you hit brick wall :wink:
See you next year! Maybe in Rust, maybe not :upside_down_face: See you next year! Maybe in Rust, maybe not :upside_down_face:
[_Advent of Code_]: https://adventofcode.com [_advent of code_]: https://adventofcode.com
[_A\*_]: https://en.wikipedia.org/wiki/A*_search_algorithm [_a\*_]: https://en.wikipedia.org/wiki/A*_search_algorithm
[`BinaryHeap`]: https://doc.rust-lang.org/std/collections/struct.BinaryHeap.html [`binaryheap`]: https://doc.rust-lang.org/std/collections/struct.BinaryHeap.html
[`Reverse`]: https://doc.rust-lang.org/std/cmp/struct.Reverse.html [`reverse`]: https://doc.rust-lang.org/std/cmp/struct.Reverse.html
[docs of the `BinaryHeap`]: https://doc.rust-lang.org/std/collections/struct.BinaryHeap.html#min-heap [docs of the `binaryheap`]: https://doc.rust-lang.org/std/collections/struct.BinaryHeap.html#min-heap

View file

@ -5,9 +5,9 @@ date: 2023-03-04T23:15
slug: leetcode/sort-diagonally slug: leetcode/sort-diagonally
authors: mf authors: mf
tags: tags:
- cpp - cpp
- leetcode - leetcode
- iterators - iterators
hide_table_of_contents: false hide_table_of_contents: false
--- ---
@ -16,7 +16,7 @@ same time.
<!--truncate--> <!--truncate-->
* Link to the problem: https://leetcode.com/problems/sort-the-matrix-diagonally/ - Link to the problem: https://leetcode.com/problems/sort-the-matrix-diagonally/
## Problem description ## Problem description
@ -131,6 +131,7 @@ advantage, given that you know how to “bend” the data structures accordingly
What does that mean for us? Well, we have an `std::sort`, we can use it, right? What does that mean for us? Well, we have an `std::sort`, we can use it, right?
Let's have a look at it: Let's have a look at it:
```cpp ```cpp
template< class RandomIt > template< class RandomIt >
void sort( RandomIt first, RandomIt last ); void sort( RandomIt first, RandomIt last );
@ -162,6 +163,7 @@ up, i.e. “_compiler-assisted development_”[^2] if you will ;)
Now we know that we can use `std::sort` to sort the diagonal itself, but we also Now we know that we can use `std::sort` to sort the diagonal itself, but we also
need to get the diagonals somehow. I'm rather lazy, so I'll just delegate it to need to get the diagonals somehow. I'm rather lazy, so I'll just delegate it to
someone else[^3]. And that way we get someone else[^3]. And that way we get
```cpp ```cpp
matrix diagonalSort(matrix mat) matrix diagonalSort(matrix mat)
{ {
@ -179,6 +181,7 @@ matrix diagonalSort(matrix mat)
This solution looks very simple, doesn't it? Well, cause it is. This solution looks very simple, doesn't it? Well, cause it is.
Let's try compiling it: Let's try compiling it:
``` ```
matrix-sort.cpp:11:23: error: use of undeclared identifier 'diagonals' [clang-diagnostic-error] matrix-sort.cpp:11:23: error: use of undeclared identifier 'diagonals' [clang-diagnostic-error]
for (auto d : diagonals(mat)) { for (auto d : diagonals(mat)) {
@ -199,9 +202,10 @@ in our matrix. We use the _for-range_ loop, so whatever we get back from the
do such functionality for a matrix of any type, not just the `int` from the challenge. do such functionality for a matrix of any type, not just the `int` from the challenge.
As I said, we need to be able to As I said, we need to be able to
* construct the object
* get the beginning - construct the object
* get the end (the “sentinel”) - get the beginning
- get the end (the “sentinel”)
```cpp ```cpp
template <typename T> template <typename T>
@ -295,10 +299,11 @@ public:
In this case we will be implementing a “simple” forward iterator, so we don't In this case we will be implementing a “simple” forward iterator, so we don't
need to implement a lot. Notably it will be: need to implement a lot. Notably it will be:
* inequality operator (we need to know when we reach the end and have nothing to
- inequality operator (we need to know when we reach the end and have nothing to
iterate over) iterate over)
* preincrementation operator (we need to be able to move around the iterable) - preincrementation operator (we need to be able to move around the iterable)
* dereference operator (we need to be able to retrieve the objects we iterate - dereference operator (we need to be able to retrieve the objects we iterate
over) over)
```cpp ```cpp
@ -376,6 +381,7 @@ After implementing the iterator over diagonals, we know that all we need to desc
a diagonal is the matrix itself and the “start” of the diagonal (row and column). a diagonal is the matrix itself and the “start” of the diagonal (row and column).
And we also know that the diagonal must provide some iterators for the `std::sort` And we also know that the diagonal must provide some iterators for the `std::sort`
function. We can start with the following skeleton: function. We can start with the following skeleton:
```cpp ```cpp
template <typename T> template <typename T>
class diagonal { class diagonal {
@ -434,12 +440,13 @@ steps.
Let's go through all of the functionality that our iterator needs to support to Let's go through all of the functionality that our iterator needs to support to
be used in `std::sort`. We need the usual operations like: be used in `std::sort`. We need the usual operations like:
* equality/inequality - equality/inequality
* incrementation - incrementation
* dereferencing - dereferencing
We will also add all the types that our iterator uses with the category of the We will also add all the types that our iterator uses with the category of the
iterator, i.e. what interface it supports: iterator, i.e. what interface it supports:
```cpp ```cpp
class diagonal_iter { class diagonal_iter {
// we need to keep reference to the matrix itself // we need to keep reference to the matrix itself
@ -486,13 +493,14 @@ public:
This is pretty similar to the previous iterator, but now we need to implement the This is pretty similar to the previous iterator, but now we need to implement the
remaining requirements of the _random access iterator_. Let's see what those are: remaining requirements of the _random access iterator_. Let's see what those are:
* decrementation - cause we need to be able to move backwards too, since _random _ - decrementation - cause we need to be able to move backwards too, since _random _
_access iterator_ extends the interface of _bidirectional iterator_ _access iterator_ extends the interface of _bidirectional iterator_
* moving the iterator in either direction by steps given as an integer - moving the iterator in either direction by steps given as an integer
* being able to tell the distance between two iterators - being able to tell the distance between two iterators
* define an ordering on the iterators - define an ordering on the iterators
Let's fill them in: Let's fill them in:
```cpp ```cpp
class diagonal_iter { class diagonal_iter {
// we need to keep reference to the matrix itself // we need to keep reference to the matrix itself
@ -575,6 +583,7 @@ public:
At this point we could probably try and compile it, right? If we do so, we will At this point we could probably try and compile it, right? If we do so, we will
get yelled at by a compiler for the following reasons: get yelled at by a compiler for the following reasons:
``` ```
/usr/bin/../lib/gcc/x86_64-redhat-linux/12/../../../../include/c++/12/bits/stl_algo.h:1792:11: error: object of type 'diagonal<int>::diagonal_iter' cannot be assigned because its copy assignment operator is implicitly deleted [clang-diagnostic-error] /usr/bin/../lib/gcc/x86_64-redhat-linux/12/../../../../include/c++/12/bits/stl_algo.h:1792:11: error: object of type 'diagonal<int>::diagonal_iter' cannot be assigned because its copy assignment operator is implicitly deleted [clang-diagnostic-error]
__last = __next; __last = __next;
@ -633,6 +642,7 @@ matrix-sort.cpp:17:19: note: copy assignment operator of 'diagonal_iter' is impl
``` ```
That's a lot of noise, isn't it? Let's focus on the important parts: That's a lot of noise, isn't it? Let's focus on the important parts:
``` ```
/usr/bin/../lib/gcc/x86_64-redhat-linux/12/../../../../include/c++/12/bits/stl_algo.h:1792:11: error: object of type 'diagonal<int>::diagonal_iter' cannot be assigned because its copy assignment operator is implicitly deleted [clang-diagnostic-error] /usr/bin/../lib/gcc/x86_64-redhat-linux/12/../../../../include/c++/12/bits/stl_algo.h:1792:11: error: object of type 'diagonal<int>::diagonal_iter' cannot be assigned because its copy assignment operator is implicitly deleted [clang-diagnostic-error]
@ -644,6 +654,7 @@ matrix-sort.cpp:17:19: note: copy assignment operator of 'diagonal_iter' is impl
Ah! We have a reference in our iterator, and this prevents us from having a copy Ah! We have a reference in our iterator, and this prevents us from having a copy
assignment operator (that is used “somewhere” in the sorting algorithm). Well… assignment operator (that is used “somewhere” in the sorting algorithm). Well…
Let's just wrap it! Let's just wrap it!
```diff ```diff
# we need to keep a different type than reference # we need to keep a different type than reference
- matrix_t& m; - matrix_t& m;

View file

@ -131,7 +131,7 @@ const config = {
for (let mapping of fallbackMapping) { for (let mapping of fallbackMapping) {
if (existingPath.includes(`/${mapping.new}/`)) { if (existingPath.includes(`/${mapping.new}/`)) {
return mapping.old.map((old) => return mapping.old.map((old) =>
existingPath.replace(`/${mapping.new}/`, `/${old}/`) existingPath.replace(`/${mapping.new}/`, `/${old}/`),
); );
} }
} }

View file

@ -1,8 +1,8 @@
import React from 'react'; import React from "react";
import clsx from 'clsx'; import clsx from "clsx";
import styles from './styles.module.css'; import styles from "./styles.module.css";
export default function ThemedSVG(props): JSX.Element { export default function ThemedSVG(props): JSX.Element {
const { source, className: parentClassName, alt, ...propsRest } = props; const { source, className: parentClassName, alt, ...propsRest } = props;

View file

@ -9,11 +9,11 @@
src: url("/fonts/CascadiaCodePLItalic.woff2") format("woff2"); src: url("/fonts/CascadiaCodePLItalic.woff2") format("woff2");
} }
[data-theme='dark'] pre, [data-theme="dark"] pre,
[data-theme='dark'] code, [data-theme="dark"] code,
[data-theme='dark'] kbd, [data-theme="dark"] kbd,
[data-theme='dark'] var, [data-theme="dark"] var,
[data-theme='dark'] tt, [data-theme="dark"] tt,
[data-theme='dark'] text { [data-theme="dark"] text {
font-weight: 350; font-weight: 350;
} }

View file

@ -16,7 +16,7 @@
} }
/* For readability concerns, you should choose a lighter palette in dark mode. */ /* For readability concerns, you should choose a lighter palette in dark mode. */
[data-theme='dark'] { [data-theme="dark"] {
--ifm-color-primary: #1d9bf0; --ifm-color-primary: #1d9bf0;
--ifm-color-primary-dark: #0f8ee3; --ifm-color-primary-dark: #0f8ee3;
--ifm-color-primary-darker: #0e86d6; --ifm-color-primary-darker: #0e86d6;
@ -32,11 +32,12 @@ kbd,
var, var,
tt, tt,
text { text {
font-family: 'Cascadia Code PL', 'JetBrains Mono', 'Iosevka', 'Fira Code', 'Hack', monospace; font-family: "Cascadia Code PL", "JetBrains Mono", "Iosevka", "Fira Code",
"Hack", monospace;
} }
[data-theme='light'] img[src$='#gh-dark-mode-only'], [data-theme="light"] img[src$="#gh-dark-mode-only"],
[data-theme='dark'] img[src$='#gh-light-mode-only'], [data-theme="dark"] img[src$="#gh-light-mode-only"],
[data-theme="light"] .dark-mode-only, [data-theme="light"] .dark-mode-only,
[data-theme="dark"] .light-mode-only { [data-theme="dark"] .light-mode-only {
display: none; display: none;

View file

@ -9,12 +9,11 @@
src: url("/fonts/JetBrainsMono-Italic[wght].woff2") format("woff2"); src: url("/fonts/JetBrainsMono-Italic[wght].woff2") format("woff2");
} }
[data-theme='dark'] pre, [data-theme="dark"] pre,
[data-theme='dark'] code, [data-theme="dark"] code,
[data-theme='dark'] kbd, [data-theme="dark"] kbd,
[data-theme='dark'] var, [data-theme="dark"] var,
[data-theme='dark'] tt, [data-theme="dark"] tt,
[data-theme='dark'] text { [data-theme="dark"] text {
font-weight: 350; font-weight: 350;
} }

View file

@ -1,201 +1,247 @@
import React from "react"; import React from "react";
import Layout from "@theme/Layout"; import Layout from "@theme/Layout";
import Contribution, { ContributionMetadata } from "../components/contributions/Contribution"; import Contribution, {
ContributionMetadata,
} from "../components/contributions/Contribution";
const contributions: ContributionMetadata[] = [ const contributions: ContributionMetadata[] = [
{ {
title: "tmt", title: "tmt",
description: (<p> description: (
<p>
The `tmt` tool provides a user-friendly way to work with tests. You can The `tmt` tool provides a user-friendly way to work with tests. You can
comfortably create new tests, safely and easily run tests across different comfortably create new tests, safely and easily run tests across
environments, review test results, debug test code and enable tests in the different environments, review test results, debug test code and enable
CI using a consistent and concise config. tests in the CI using a consistent and concise config.
</p>), </p>
contribution: (<p> ),
Just a smallish contribution to the docs related to the changes implemented contribution: (
on the Packit side. <p>
</p>), Just a smallish contribution to the docs related to the changes
implemented on the Packit side.
</p>
),
repoURL: "https://github.com/teemtee/tmt", repoURL: "https://github.com/teemtee/tmt",
}, },
{ {
title: "Fedora Infrastructure Ansible", title: "Fedora Infrastructure Ansible",
description: (<p> description: (
<p>
Collection of Ansible playbooks that powers the Fedora Infrastructure. Collection of Ansible playbooks that powers the Fedora Infrastructure.
</p>), </p>
contribution: (<p> ),
I have adjusted the groups in the Bodhi playbooks after Packit has contribution: (
been granted the privileges to propose updates without restrictions. <p>
</p>), I have adjusted the groups in the Bodhi playbooks after Packit has been
granted the privileges to propose updates without restrictions.
</p>
),
repoURL: "https://pagure.io/fedora-infra/ansible", repoURL: "https://pagure.io/fedora-infra/ansible",
}, },
{ {
title: "Bodhi", title: "Bodhi",
description: (<p> description: (
Bodhi is a web-system that facilitates the process of publishing <p>
updates for a Fedora-based software distribution. Bodhi is a web-system that facilitates the process of publishing updates
</p>), for a Fedora-based software distribution.
contribution: (<p> </p>
I have adjusted the client, so that it doesn't show secrets in ),
terminal when you log in to the Bodhi via browser. contribution: (
</p>), <p>
I have adjusted the client, so that it doesn't show secrets in terminal
when you log in to the Bodhi via browser.
</p>
),
repoURL: "https://github.com/fedora-infra/bodhi", repoURL: "https://github.com/fedora-infra/bodhi",
}, },
{ {
title: "Gluetool Modules Collection", title: "Gluetool Modules Collection",
description: (<p> description: (
<p>
Modules for <code>gluetool</code> a command line centric framework Modules for <code>gluetool</code> a command line centric framework
usable for glueing modules into a pipeline. usable for glueing modules into a pipeline.
</p>), </p>
contribution: (<ul> ),
contribution: (
<ul>
<li> <li>
I have proposed a possible implementation of git merging that I have proposed a possible implementation of git merging that was
was later on extended. later on extended.
</li> </li>
<li> <li>
I have tried to help out with Copr module after they deprecated I have tried to help out with Copr module after they deprecated older
older version of their API. version of their API.
</li> </li>
</ul>), </ul>
),
repoURL: "https://gitlab.com/testing-farm/gluetool-modules", repoURL: "https://gitlab.com/testing-farm/gluetool-modules",
}, },
{ {
title: "Pagure", title: "Pagure",
description: (<p> description: (
Pagure is a git-centered forge, python based using pygit2. <p>Pagure is a git-centered forge, python based using pygit2.</p>
</p>), ),
contribution: (<p> contribution: (
I have added an API endpoint for reopening pull requests. <p>I have added an API endpoint for reopening pull requests.</p>
</p>), ),
repoURL: "https://pagure.io/pagure", repoURL: "https://pagure.io/pagure",
}, },
{ {
title: "Copr", title: "Copr",
description: (<p> description: (
<p>
RPM build system - upstream for{" "} RPM build system - upstream for{" "}
<a target="_blank" href="https://copr.fedorainfracloud.org/">Copr</a>. <a target="_blank" href="https://copr.fedorainfracloud.org/">
</p>), Copr
contribution: (<ul> </a>
.
</p>
),
contribution: (
<ul>
<li>Supporting external repositories for custom SRPM build method.</li>
<li> <li>
Supporting external repositories for custom SRPM build method. Allowing admins of Copr repositories to build without the need to ask
for explicit <code>builder</code> permissions.
</li> </li>
<li> </ul>
Allowing admins of Copr repositories to build without the need ),
to ask for explicit <code>builder</code> permissions.
</li>
</ul>),
repoURL: "https://github.com/fedora-copr/copr", repoURL: "https://github.com/fedora-copr/copr",
}, },
{ {
title: "python-gitlab", title: "python-gitlab",
description: (<p> description: <p>A python wrapper for the GitLab API.</p>,
A python wrapper for the GitLab API. contribution: (
</p>), <p>
contribution: (<p>
I have contributed support for the <code>merge_ref</code> on merge I have contributed support for the <code>merge_ref</code> on merge
requests that hasn't been supported, yet it was present in the GitLab requests that hasn't been supported, yet it was present in the GitLab
API. API.
</p>), </p>
),
repoURL: "https://github.com/python-gitlab/python-gitlab", repoURL: "https://github.com/python-gitlab/python-gitlab",
}, },
{ {
title: "PatternFly React", title: "PatternFly React",
description: (<p> description: <p>A set of React components for the PatternFly project.</p>,
A set of React components for the PatternFly project. contribution: (
</p>), <p>
contribution: (<p> When working on Packit Dashboard, I have spotted smaller bugs that were
When working on Packit Dashboard, I have spotted smaller bugs that present in this project and fixed them upstream to provide better
were present in this project and fixed them upstream to provide experience for our users.
better experience for our users. </p>
</p>), ),
repoURL: "https://github.com/patternfly/patternfly-react", repoURL: "https://github.com/patternfly/patternfly-react",
}, },
{ {
title: "Fira Code", title: "Fira Code",
description: (<p> description: <p>Free monospaced font with programming ligatures.</p>,
Free monospaced font with programming ligatures. contribution: (
</p>), <p>
contribution: (<p> I have set up a GitHub Action for building the font on each push to the
I have set up a GitHub Action for building the font on each push to default branch allowing users to install <i>bleeding edge</i> version of
the default branch allowing users to install <i>bleeding edge</i>{" "} the font.
version of the font. </p>
</p>), ),
repoURL: "https://github.com/tonsky/FiraCode", repoURL: "https://github.com/tonsky/FiraCode",
}, },
{ {
title: "nixpkgs", title: "nixpkgs",
description: (<p> description: (
<p>
Nixpkgs is a collection of over 80,000 software packages that can be Nixpkgs is a collection of over 80,000 software packages that can be
installed with the Nix package manager. It also implements NixOS, installed with the Nix package manager. It also implements NixOS, a
a purely-functional Linux distribution. purely-functional Linux distribution.
</p>), </p>
contribution: (<p> ),
When I was trying out the nixpkgs, I have tried to bump .NET Core to contribution: (
the latest version. My changes haven't been accepted as they required <p>
When I was trying out the nixpkgs, I have tried to bump .NET Core to the
latest version. My changes haven't been accepted as they required
bumping of multiple more packages that depended upon the .NET Core. bumping of multiple more packages that depended upon the .NET Core.
</p>), </p>
),
repoURL: "https://github.com/NixOS/nixpkgs", repoURL: "https://github.com/NixOS/nixpkgs",
}, },
{ {
title: "Darcula", title: "Darcula",
description: (<p> description: (
<p>
A theme for Visual Studio Code based on Darcula theme from Jetbrains A theme for Visual Studio Code based on Darcula theme from Jetbrains
IDEs. IDEs.
</p>), </p>
contribution: (<p> ),
contribution: (
<p>
I have contributed support for diff files, though the project doesn't I have contributed support for diff files, though the project doesn't
seem to be live anymore, so it hasn't been accepted as of now. seem to be live anymore, so it hasn't been accepted as of now.
</p>), </p>
),
repoURL: "https://github.com/rokoroku/vscode-theme-darcula", repoURL: "https://github.com/rokoroku/vscode-theme-darcula",
}, },
{ {
title: "Packit", title: "Packit",
description: (<p> description: (
An open source project aiming to ease the integration of your <p>
project with Fedora Linux, CentOS Stream and other distributions. An open source project aiming to ease the integration of your project
</p>), with Fedora Linux, CentOS Stream and other distributions.
contribution: (<p> </p>
),
contribution: (
<p>
Have a look at my{" "} Have a look at my{" "}
<a <a
href="https://github.com/search?q=is%3Apr%20author%3Amfocko%20org%3Apackit&type=pullrequests" href="https://github.com/search?q=is%3Apr%20author%3Amfocko%20org%3Apackit&type=pullrequests"
target="_blank"> target="_blank"
>
pull requests pull requests
</a>. </a>
</p>), .
</p>
),
repoURL: "https://github.com/packit", repoURL: "https://github.com/packit",
}, },
{ {
title: "Snitch", title: "Snitch",
description: (<> description: (
<>
<p> <p>
Language agnostic tool that collects TODOs in the source code Language agnostic tool that collects TODOs in the source code and
and reports them as Issues. reports them as Issues.
</p> </p>
</>), </>
contribution: (<ul> ),
contribution: (
<ul>
<li>Environment variable support for self-hosted GitLab instances</li> <li>Environment variable support for self-hosted GitLab instances</li>
<li>GitLab support</li> <li>GitLab support</li>
</ul>), </ul>
),
repoURL: "https://github.com/tsoding/snitch", repoURL: "https://github.com/tsoding/snitch",
}, },
{ {
title: "Karel the Robot", title: "Karel the Robot",
description: (<> description: (
<>
<p> <p>
Karel the robot is in general an educational programming Karel the robot is in general an educational programming language for
language for beginners, created by <i>Richard E. Pattis</i>. beginners, created by <i>Richard E. Pattis</i>. This is implementation
This is implementation of <i>Karel the Robot</i> for{" "} of <i>Karel the Robot</i> for <i>C programming language</i>.
<i>C programming language</i>.
</p> </p>
<p> <p>
This project is used for educational purposes at{" "} This project is used for educational purposes at{" "}
<a target="_blank" href="https://fei.tuke.sk">TUKE</a>. <a target="_blank" href="https://fei.tuke.sk">
TUKE
</a>
.
</p> </p>
</>), </>
contribution: (<p> ),
I have contributed some refactoring tips to the author of the contribution: (
library. <p>
</p>), I have contributed some refactoring tips to the author of the library.
</p>
),
repoURL: "https://git.kpi.fei.tuke.sk/kpi/karel-the-robot", repoURL: "https://git.kpi.fei.tuke.sk/kpi/karel-the-robot",
}, },
]; ];
@ -212,7 +258,10 @@ export default function Contributions(): JSX.Element {
<div className="row"> <div className="row">
{contributions.map((contributionData) => ( {contributions.map((contributionData) => (
<Contribution key={contributionData.project} {...contributionData} /> <Contribution
key={contributionData.project}
{...contributionData}
/>
))} ))}
</div> </div>
</main> </main>

View file

@ -3,93 +3,68 @@
// import type { PrismTheme } from '../types' // import type { PrismTheme } from '../types'
var theme/* : PrismTheme */ = { var theme: PrismTheme = {
"plain": { plain: {
"color": "#657b83", color: "#657b83",
"backgroundColor": "#fdf6e3" backgroundColor: "#fdf6e3",
}, },
"styles": [ styles: [
{ {
"types": [ types: ["comment"],
"comment" style: {
], color: "rgb(147, 161, 161)",
"style": { fontStyle: "italic",
"color": "rgb(147, 161, 161)", },
"fontStyle": "italic"
}
}, },
{ {
"types": [ types: ["string"],
"string" style: {
], color: "rgb(42, 161, 152)",
"style": { },
"color": "rgb(42, 161, 152)"
}
}, },
{ {
"types": [ types: ["number"],
"number" style: {
], color: "rgb(211, 54, 130)",
"style": { },
"color": "rgb(211, 54, 130)"
}
}, },
{ {
"types": [ types: ["variable", "function", "tag"],
"variable", style: {
"function", color: "rgb(38, 139, 210)",
"tag" },
],
"style": {
"color": "rgb(38, 139, 210)"
}
}, },
{ {
"types": [ types: ["class-name", "keyword", "char", "constant", "changed"],
"class-name", style: {
"keyword", color: "rgb(203, 75, 22)",
"char", },
"constant",
"changed"
],
"style": {
"color": "rgb(203, 75, 22)"
}
}, },
{ {
"types": [ types: ["punctuation", "inserted"],
"punctuation", style: {
"inserted" color: "rgb(133, 153, 0)",
], },
"style": {
"color": "rgb(133, 153, 0)"
}
}, },
{ {
"types": [ types: ["builtin"],
"builtin" style: {
], color: "rgb(181, 137, 0)",
"style": { },
"color": "rgb(181, 137, 0)"
}
}, },
{ {
"types": [ types: ["attr-name"],
"attr-name" style: {
], color: "rgb(147, 161, 161)",
"style": { },
"color": "rgb(147, 161, 161)"
}
}, },
{ {
"types": [ types: ["deleted"],
"deleted" style: {
color: "rgb(220, 50, 47)",
},
},
], ],
"style": {
"color": "rgb(220, 50, 47)"
}
}
]
}; };
module.exports = theme; module.exports = theme;