From d16eaafd976ca3df42aab2725c6ccc540c797036 Mon Sep 17 00:00:00 2001 From: Matej Focko Date: Mon, 1 Jan 2024 18:13:30 +0100 Subject: [PATCH 01/10] algorithms(bf-to-astar): add intro Signed-off-by: Matej Focko --- .../11-paths/2024-01-01-bf-to-astar/index.md | 169 ++++++++++++++++++ .../algorithms/paths/bf-to-astar/.archive | 0 .../algorithms/paths/bf-to-astar/graph.hpp | 84 +++++++++ .../algorithms/paths/bf-to-astar/main.cpp | 34 ++++ .../algorithms/paths/bf-to-astar/makefile | 8 + 5 files changed, 295 insertions(+) create mode 100644 algorithms/11-paths/2024-01-01-bf-to-astar/index.md create mode 100644 static/files/algorithms/paths/bf-to-astar/.archive create mode 100644 static/files/algorithms/paths/bf-to-astar/graph.hpp create mode 100644 static/files/algorithms/paths/bf-to-astar/main.cpp create mode 100644 static/files/algorithms/paths/bf-to-astar/makefile diff --git a/algorithms/11-paths/2024-01-01-bf-to-astar/index.md b/algorithms/11-paths/2024-01-01-bf-to-astar/index.md new file mode 100644 index 0000000..4b79929 --- /dev/null +++ b/algorithms/11-paths/2024-01-01-bf-to-astar/index.md @@ -0,0 +1,169 @@ +--- +id: index +slug: /paths/bf-to-astar +title: From BF to A* +description: | + Figuring out shortest-path problem from the BF to the A* algorithm. +tags: +- cpp +- brute force +- bellman ford +- dynamic programming +- dijkstra +- a star +last_update: + date: 2024-01-01 +--- + +## Intro + +We will delve into the details and ideas of the most common path-finding +algorithms. For the purpose of demonstrating some “features” of the improved +algorithms, we will use a 2D map with some rules that will allow us to show cons +and pros of the shown algorithms. + +Let's have a look at the example map: +``` +############# +#..#..*.*.**# +##***.....**# +#..########.# +#...###...#.# +#..#...##.#.# +#..#.*.#..#.# +#....#....#.# +########*.*.# +#...........# +############# +``` + +We can see three different kinds of cells: +1. `#` which represent walls, that cannot be entered at all +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 + base price of moving around the map) + +Let's dissect a specific position on the map to get a better grasp of the rules: +``` + . +#S* + . +``` +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 east (`*`) is allowed with the cost of 5 coins, and finally +* move to the south (`.`) with the cost of 1 coin. + +:::info + +Further on I will follow the same scheme for marking cells with an addition of +`D` to denote the _destination_ to which we will be finding the shortest path. + +::: + +## Boilerplate + +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: +* remembers the costs of moving around +* provides a simple function that returns price for moving **directly** between + two positions on the map +* allows us to print the map out, just in case we'd need some adjustments to be + made + +We can see the `graph` header here: +```cpp +#ifndef _GRAPH_HPP +#define _GRAPH_HPP + +#include +#include +#include +#include +#include + +using vertex_t = std::pair; + +struct graph { + graph(const std::vector>& map) + : map(map), + _height(static_cast(map.size())), + _width(map.empty() ? 0 : static_cast(map[0].size())) {} + + static auto unreachable() -> int { return UNREACHABLE; } + static auto normal_cost() -> int { return NORMAL_COST; } + static auto vortex_cost() -> int { return VORTEX_COST; } + + auto cost(const vertex_t& u, const vertex_t& v) const -> int { + auto [ux, uy] = u; + auto [vx, vy] = v; + + auto hd = std::abs(ux - vx) + std::abs(uy - vy); + switch (hd) { + // ‹u = v›; staying on the same cell + case 0: + return 0; + // ‹u› and ‹v› are neighbours + case 1: + break; + // ‹u› and ‹v› are not neighbouring cells + default: + return UNREACHABLE; + } + + // boundary check + if (vy < 0 || vy >= _height || vx < 0 || vx >= _width) { + return UNREACHABLE; + } + + switch (map[vy][vx]) { + case '#': + return UNREACHABLE; + case '*': + return VORTEX_COST; + default: + return NORMAL_COST; + } + } + + auto width() const -> int { return _width; } + auto height() const -> int { return _height; } + auto has(const vertex_t& v) const -> bool { + auto [x, y] = v; + return (0 <= y && y < _height) && (0 <= x && x < _width); + } + + friend std::ostream& operator<<(std::ostream& os, const graph& g); + + private: + std::vector> map; + int _height, _width; + + const static int UNREACHABLE = std::numeric_limits::max(); + // XXX: modify here to change the price of entering the vortex + const static int VORTEX_COST = 5; + const static int NORMAL_COST = 1; +}; + +std::ostream& operator<<(std::ostream& os, const graph& g) { + for (const auto& row : g.map) { + for (const char cell : row) { + os << cell; + } + os << "\n"; + } + + return os; +} + +#endif /* _GRAPH_HPP */ +``` + +:::info Source code + +**TODO** link the sources + +::: + +Let's finally start with some algorithms! diff --git a/static/files/algorithms/paths/bf-to-astar/.archive b/static/files/algorithms/paths/bf-to-astar/.archive new file mode 100644 index 0000000..e69de29 diff --git a/static/files/algorithms/paths/bf-to-astar/graph.hpp b/static/files/algorithms/paths/bf-to-astar/graph.hpp new file mode 100644 index 0000000..1bdebcd --- /dev/null +++ b/static/files/algorithms/paths/bf-to-astar/graph.hpp @@ -0,0 +1,84 @@ +#ifndef _GRAPH_HPP +#define _GRAPH_HPP + +#include +#include +#include +#include +#include + +using vertex_t = std::pair; + +struct graph { + graph(const std::vector>& map) + : map(map), + _height(static_cast(map.size())), + _width(map.empty() ? 0 : static_cast(map[0].size())) {} + + static auto unreachable() -> int { return UNREACHABLE; } + static auto normal_cost() -> int { return NORMAL_COST; } + static auto vortex_cost() -> int { return VORTEX_COST; } + + auto cost(const vertex_t& u, const vertex_t& v) const -> int { + auto [ux, uy] = u; + auto [vx, vy] = v; + + auto md = std::abs(ux - vx) + std::abs(uy - vy); + switch (md) { + // ‹u = v›; staying on the same cell + case 0: + return 0; + // ‹u› and ‹v› are neighbours + case 1: + break; + // ‹u› and ‹v› are not neighbouring cells + default: + return UNREACHABLE; + } + + // boundary check + if (vy < 0 || vy >= _height || vx < 0 || vx >= _width) { + return UNREACHABLE; + } + + switch (map[vy][vx]) { + case '#': + return UNREACHABLE; + case '*': + return VORTEX_COST; + default: + return NORMAL_COST; + } + } + + auto width() const -> int { return _width; } + auto height() const -> int { return _height; } + auto has(const vertex_t& v) const -> bool { + auto [x, y] = v; + return (0 <= y && y < _height) && (0 <= x && x < _width); + } + + friend std::ostream& operator<<(std::ostream& os, const graph& g); + + private: + std::vector> map; + int _height, _width; + + const static int UNREACHABLE = std::numeric_limits::max(); + // XXX: modify here to change the price of entering the vortex + const static int VORTEX_COST = 5; + const static int NORMAL_COST = 1; +}; + +std::ostream& operator<<(std::ostream& os, const graph& g) { + for (const auto& row : g.map) { + for (const char cell : row) { + os << cell; + } + os << "\n"; + } + + return os; +} + +#endif /* _GRAPH_HPP */ diff --git a/static/files/algorithms/paths/bf-to-astar/main.cpp b/static/files/algorithms/paths/bf-to-astar/main.cpp new file mode 100644 index 0000000..9b02c28 --- /dev/null +++ b/static/files/algorithms/paths/bf-to-astar/main.cpp @@ -0,0 +1,34 @@ +#include +#include +#include +#include + +#include "astar.hpp" +#include "bf.hpp" +#include "dijkstra.hpp" +#include "graph.hpp" + +auto line_to_vector(const std::string& l) -> std::vector { + return std::vector(l.begin(), l.end()); +} + +auto main() -> int { + graph g{std::vector{ + line_to_vector(std::string("#############")), + line_to_vector(std::string("#..#..*.*.**#")), + line_to_vector(std::string("##***.....**#")), + line_to_vector(std::string("#..########.#")), + line_to_vector(std::string("#...###...#.#")), + line_to_vector(std::string("#..#...##.#.#")), + line_to_vector(std::string("#..#.*.#..#.#")), + line_to_vector(std::string("#....#....#.#")), + line_to_vector(std::string("########*.*.#")), + line_to_vector(std::string("#...........#")), + line_to_vector(std::string("#############")), + }}; + std::cout << "Normal cost: " << g.normal_cost() << "\n"; + std::cout << "Vortex cost: " << g.vortex_cost() << "\n"; + std::cout << "Graph:\n" << g; + + return 0; +} diff --git a/static/files/algorithms/paths/bf-to-astar/makefile b/static/files/algorithms/paths/bf-to-astar/makefile new file mode 100644 index 0000000..8769437 --- /dev/null +++ b/static/files/algorithms/paths/bf-to-astar/makefile @@ -0,0 +1,8 @@ +CXX=c++ +CXXFLAGS=-std=c++20 -Wall -Wextra -g + +all: format + $(CXX) $(CXXFLAGS) main.cpp -o main + +format: + clang-format -i -style=google *.hpp *.cpp From 858a96722b55fac6747b6547fbd90d6d89e886ec Mon Sep 17 00:00:00 2001 From: Matej Focko Date: Mon, 1 Jan 2024 18:21:24 +0100 Subject: [PATCH 02/10] algorithms(bf-to-astar): add BF Signed-off-by: Matej Focko --- .../11-paths/2024-01-01-bf-to-astar/01-bf.md | 546 ++++++++++++++++++ .../files/algorithms/paths/bf-to-astar/bf.hpp | 140 +++++ .../algorithms/paths/bf-to-astar/main.cpp | 7 + .../paths/bf-to-astar/bellman-ford-meme.jpg | Bin 0 -> 37548 bytes 4 files changed, 693 insertions(+) create mode 100644 algorithms/11-paths/2024-01-01-bf-to-astar/01-bf.md create mode 100644 static/files/algorithms/paths/bf-to-astar/bf.hpp create mode 100644 static/img/algorithms/paths/bf-to-astar/bellman-ford-meme.jpg diff --git a/algorithms/11-paths/2024-01-01-bf-to-astar/01-bf.md b/algorithms/11-paths/2024-01-01-bf-to-astar/01-bf.md new file mode 100644 index 0000000..ca9d228 --- /dev/null +++ b/algorithms/11-paths/2024-01-01-bf-to-astar/01-bf.md @@ -0,0 +1,546 @@ +--- +id: bf +slug: /paths/bf-to-astar/bf +title: BF +description: | + Solving the shortest path problem with a naïve approach that turns into + something. +tags: +- cpp +- brute force +- bellman ford +- dynamic programming +last_update: + date: 2024-01-01 +--- + +## Basic idea + +We will _ease in_ with our own algorithm to find the shortest path. We will +start by thinking about the ways we can achieve that. If we didn't have the `*` +cells, we could've easily run a BFS[^1] and be done with it. Maybe it is a good +place to start, or isn't, there is only one way to find out though. + +_How does the BFS work?_ We know the vertex where we start and we know the +vertex we want to find the shortest path to. Given this knowledge we +incrementally visit all of our neighbours and we do that over and over until the +destination is found[^2]. Could we leverage this somehow? + +## Naïve approach + +Well, we could probably start with all vertices being _unreachable_ (having the +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 +going on repeat, we will name this function `bf()` as in _brute-force_, cause it +is trying to find it the hard way: +```cpp +const static std::vector DIRECTIONS = + std::vector{std::make_pair(0, 1), std::make_pair(0, -1), + std::make_pair(1, 0), std::make_pair(-1, 0)}; + +auto bf(const graph& g, const vertex_t& source, const vertex_t& destination) + -> int { + // ‹source› must be within the bounds + assert(g.has(source)); + + // ‹destination› must be within the bounds + assert(g.has(destination)); + + // we need to initialize the distances + std::vector> distances( + g.height(), std::vector(g.width(), graph::unreachable())); + + // ‹source› destination denotes the beginning where the cost is 0 + auto [sx, sy] = source; + distances[sy][sx] = 0; + + // now we need to improve the paths as long as possible + bool improvement_found; + do { + // reset the flag at the beginning + improvement_found = false; + + // go through all of the vertices + for (int y = 0; y < g.height(); ++y) { + for (int x = 0; x < g.width(); ++x) { + // skip the cells we cannot reach + if (distances[y][x] == graph::unreachable()) { + continue; + } + + // go through the neighbours + auto u = std::make_pair(x, y); + for (const auto& [dx, dy] : DIRECTIONS) { + auto v = std::make_pair(x + dx, y + dy); + auto cost = g.cost(u, v); + + // if we can move to the cell and it's better, relax¹ it + if (cost != graph::unreachable() && + distances[y][x] + cost < distances[y + dy][x + dx]) { + distances[y + dy][x + dx] = distances[y][x] + cost; + improvement_found = true; + } + } + } + } + } while (improvement_found); + + return distances[destination.second][destination.first]; +} +``` + +:::info Relaxation + +I have made a brief mention of the relaxation in the comment in the code. You've +been probably thought that **relaxation of an edge** means that you found +a better solution to the problem. + +In general it is an approximation technique that _reduces_ the problem of +finding the path `u → x1 → … → xn → v` to subproblems +`u → x1, x1 → x2, …, xn → v` such that the sum of the costs of each step is +**minimal**. + +::: + +### Correctness + +_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: +``` +Graph: +############# +#..#..*.*.**# +##***.....**# +#..########.# +#...###...#.# +#..#...##.#.# +#..#.*.#..#.# +#D...#....#.# +########*.*.# +#S..........# +############# +Cost: 22 +``` + +If you have a better look at the map, you will realize that the cost `22` is the +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 +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. +``` +Graph: +############# +#..#..*.*.**# +##***.....**# +#..########.# +#...###...#.# +#..#...##.#.# +#..#.*.#..#.# +#D...#....#.# +########*.*.# +#S..........# +############# +``` + +And we're somehow stuck… The issue comes from the fact that _spinning around_ in +the vortices allows us to lower the cost infinitely. That's why after each +iteration there is still a possibility to lower the cost, hence the algorithm +doesn't finish. _What can we do about this?_ + +:::tip + +This algorithm is correct as long as there are no negative loops, i.e. ways how +to lower the cost infinitely. Therefore we can also just lay a precondition that +requires no negative loops to be present. + +::: + +### Fixing the infinite loop + +Our issue lies in the fact that we can endlessly lower the cost. Such thing must +surely happen in some kind of a loop. We could probably track the relaxations +and once we spot repeating patterns, we know we can safely terminate with _some_ +results at least. + +This approach will not even work on our 2D map, let alone any graph. Problem is +that the _negative loops_ lower the cost in **each** iteration and that results +in lowering of the costs to the cells that are reachable from the said loops. +That's why this problem is relatively hard to tackle, it's not that easy to spot +the repeating patterns algorithmically. + +On the other hand, we can approach this from the different perspective. Let's +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$ +> 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 +> cost. +> +> In such scenario assume the worst-case ordering of the relaxations (only one +> _helpful_ relaxation per iteration). In this case, in each iteration we find +> the next edge on our path $P$ as the last. This means that we need +> $\vert V \vert - 1$ iterations to find the shortest path $P$. +> +> Because we have laid $P$ as the shortest path from $v_1$ to $v_n$ and it +> visits all vertices, its prefixes are the shortest paths from $v_1$ to any +> other vertex in our graph. +> +> Therefore, we can safely assume that any relaxation after $\vert V \vert - 1$ +> iterations, is the effect of a negative loop in the graph. + +_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: +```cpp +auto bf_finite(const graph& g, const vertex_t& source, + const vertex_t& destination) -> int { + // ‹source› must be within the bounds + assert(g.has(source)); + + // ‹destination› must be within the bounds + assert(g.has(destination)); + + // we need to initialize the distances + std::vector> distances( + g.height(), std::vector(g.width(), graph::unreachable())); + + // ‹source› destination denotes the beginning where the cost is 0 + auto [sx, sy] = source; + distances[sy][sx] = 0; + + // now we only iterate as many times as cells that we have + for (int i = g.height() * g.width(); i > 0; --i) { + // go through all of the vertices + for (int y = 0; y < g.height(); ++y) { + for (int x = 0; x < g.width(); ++x) { + // skip the cells we cannot reach + if (distances[y][x] == graph::unreachable()) { + continue; + } + + // go through the neighbours + auto u = std::make_pair(x, y); + for (const auto& [dx, dy] : DIRECTIONS) { + auto v = std::make_pair(x + dx, y + dy); + auto cost = g.cost(u, v); + + // if we can move to the cell and it's better, relax¹ it + if (cost != graph::unreachable() && + distances[y][x] + cost < distances[y + dy][x + dx]) { + distances[y + dy][x + dx] = distances[y][x] + cost; + } + } + } + } + } + + return distances[destination.second][destination.first]; +} +``` + +And we get the following result: +``` +Graph: +############# +#..#..*.*.**# +##***.....**# +#..########.# +#...###...#.# +#..#...##.#.# +#..#.*.#..#.# +#D...#....#.# +########*.*.# +#S..........# +############# +Cost: -236 +``` + +The negative cost means that there is a way to _propel_ ourselves via some +vortices. Let's adjust the cost of _vortices_ back to the original `5` and check +whether our modified algorithm works as it did before. And it surely does yield +the `22` as before. + +:::tip Refactoring + +You can definitely notice some _deep nesting_ in our code, to counter this +phenomenon I will convert the looping over `x` and `y` to one variable that can +be decomposed to `x` and `y`. It is a very common practice when working with 2D +arrays/lists to represent them as 1D. In our case: + +``` +i : 0 → width * height - 1 +x = i % width +y = i / width +``` + +::: + +## Bellman-Ford + +If you have ever attended any Algorithms course that had path-finding in its +sylabus, you probably feel like you've seen the algorithm above before[^3]… And +yes, the first algorithm I have proposed is a very dumb version of the +_Bellman-Ford_ algorithm, it's dumb, because it loops :wink: After our “looping” +prevention we got to the point that is almost the _Bellman-Ford_ with the one +exception that it doesn't report whether there are any negative cycles, it just +ends. + +Let's have a look at a proper implementation of the Bellman-Ford algorithm: +```cpp +auto bellman_ford(const graph& g, const vertex_t& source) + -> std::vector> { + // ‹source› must be within the bounds + assert(g.has(source)); + + // we need to initialize the distances + std::vector> distances( + g.height(), std::vector(g.width(), graph::unreachable())); + + // ‹source› destination denotes the beginning where the cost is 0 + auto [sx, sy] = source; + distances[sy][sx] = 0; + + // now we only iterate as many times as cells that we have + for (int i = g.height() * g.width(); i > 0; --i) { + // go through all of the vertices + for (int v = g.height() * g.width() - 1; v >= 0; --v) { + int y = v / g.width(); + int x = v % g.width(); + + // skip the cells we cannot reach + if (distances[y][x] == graph::unreachable()) { + continue; + } + + // go through the neighbours + auto u = std::make_pair(x, y); + for (const auto& [dx, dy] : DIRECTIONS) { + auto v = std::make_pair(x + dx, y + dy); + auto cost = g.cost(u, v); + + // if we can move to the cell and it's better, relax¹ it + if (cost != graph::unreachable() && + distances[y][x] + cost < distances[y + dy][x + dx]) { + distances[y + dy][x + dx] = distances[y][x] + cost; + } + } + } + } + + // now we check for the negative loops + bool relaxed = false; + for (int v = g.height() * g.width() - 1; !relaxed && v >= 0; --v) { + int y = v / g.width(); + int x = v % g.width(); + + // skip the cells we cannot reach + if (distances[y][x] == graph::unreachable()) { + continue; + } + + // go through the neighbours + auto u = std::make_pair(x, y); + for (const auto& [dx, dy] : DIRECTIONS) { + auto v = std::make_pair(x + dx, y + dy); + auto cost = g.cost(u, v); + + // if we can move to the cell and it's better, relax¹ it + if (cost != graph::unreachable() && + distances[y][x] + cost < distances[y + dy][x + dx]) { + relaxed = true; + std::cerr << "Found a negative loop\n"; + break; + } + } + } + + return distances; +} +``` + +And if we run it with our negative cost of entering vortices: +``` +[Bellman-Ford] Found a negative loop +[Bellman-Ford] Cost: -240 +``` + +### On the Bellman-Ford + +You might be surprised that we have managed to iterate from a brute-force method +that mindlessly tries to find a better path until there are no better paths left +all the way to the Bellman-Ford algorithm. + +I always say that Bellman-Ford is a _smart_ brute-force. BF is also an algorithm +that leverages _dynamic programming_. You might wonder how can it utilize DP if +it is “technically” a brute-force technique. Table with the shortest distances +is the thing that makes it DP. + +> I might not know the shortest path yet, but I do remember all of other paths, +> and I can improve them, if possible. + +That's where the beauty of both _dynamic programming_ and _relaxing_ gets merged +together and does its magic. + +Proof of the correctness of the BF is done via induction to the number of +iterations. I would suggest to try to prove the correctness yourself and +possibly look it up, if necessary. + +Also the correctness of the BF relies on the conclusion we've made when fixing +the infinite-loop on our naïve BF solution. + +## Small refactor + +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: +```cpp +static auto _check_vertex(const graph& g, + std::vector>& distances, int v, + bool check_only = false) -> bool { + bool improvement_found = false; + + // unpack the vertex coordinates + int y = v / g.width(); + int x = v % g.width(); + + // skip the cells we cannot reach + if (distances[y][x] == graph::unreachable()) { + return false; + } + + // go through the neighbours + auto u = std::make_pair(x, y); + for (const auto& [dx, dy] : DIRECTIONS) { + auto v = std::make_pair(x + dx, y + dy); + auto cost = g.cost(u, v); + + // if we can move to the cell and it's better, relax¹ it + if (cost != graph::unreachable() && + distances[y][x] + cost < distances[y + dy][x + dx]) { + if (check_only) { + return true; + } + + distances[y + dy][x + dx] = distances[y][x] + cost; + improvement_found = true; + } + } + + return improvement_found; +} +``` + +This function can be also used for checking the negative loops at the end of the +BF by using the `check_only` parameter to signal that we just want to know if +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 +path-finding algorithms in a clear way: +```cpp +auto bf(const graph& g, const vertex_t& source, const vertex_t& destination) + -> int { + // ‹source› must be within the bounds + assert(g.has(source)); + + // ‹destination› must be within the bounds + assert(g.has(destination)); + + // we need to initialize the distances + std::vector> distances( + g.height(), std::vector(g.width(), graph::unreachable())); + + // ‹source› destination denotes the beginning where the cost is 0 + auto [sx, sy] = source; + distances[sy][sx] = 0; + + // now we need to improve the paths as long as possible + bool improvement_found; + do { + // reset the flag at the beginning + improvement_found = false; + + // go through all of the vertices + for (int v = g.height() * g.width() - 1; v >= 0; --v) { + improvement_found = _check_vertex(g, distances, v) || improvement_found; + } + } while (improvement_found); + + return distances[destination.second][destination.first]; +} + +auto bf_finite(const graph& g, const vertex_t& source, + const vertex_t& destination) -> int { + // ‹source› must be within the bounds + assert(g.has(source)); + + // ‹destination› must be within the bounds + assert(g.has(destination)); + + // we need to initialize the distances + std::vector> distances( + g.height(), std::vector(g.width(), graph::unreachable())); + + // ‹source› destination denotes the beginning where the cost is 0 + auto [sx, sy] = source; + distances[sy][sx] = 0; + + // now we only iterate as many times as cells that we have + for (int i = g.height() * g.width(); i > 0; --i) { + // go through all of the vertices + for (int v = g.height() * g.width() - 1; v >= 0; --v) { + _check_vertex(g, distances, v); + } + } + + return distances[destination.second][destination.first]; +} + +auto bellman_ford(const graph& g, const vertex_t& source) + -> std::vector> { + // ‹source› must be within the bounds + assert(g.has(source)); + + // we need to initialize the distances + std::vector> distances( + g.height(), std::vector(g.width(), graph::unreachable())); + + // ‹source› destination denotes the beginning where the cost is 0 + auto [sx, sy] = source; + distances[sy][sx] = 0; + + // now we only iterate as many times as cells that we have + for (int i = g.height() * g.width(); i > 0; --i) { + // go through all of the vertices + for (int v = g.height() * g.width() - 1; v >= 0; --v) { + _check_vertex(g, distances, v); + } + } + + // now we check for the negative loops + for (int v = g.height() * g.width() - 1; v >= 0; --v) { + if (_check_vertex(g, distances, v, true)) { + std::cerr << "[Bellman-Ford] Found a negative loop\n"; + break; + } + } + + return distances; +} + +``` + +--- + +:::tip + +You might've noticed that I've been using abbreviation _BF_ interchangeably for +both _Bellman-Ford_ and _brute-force_. If you think about the way Bellman-Ford +algorithm works, you should realize that in the worst case it's updating the +shortest path till there no shorter path exists, so in a sense, you could really +consider it a brute-force algorithm. + +::: + + +[^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 + vertices to not taint the shortest path by already visited vertices. +[^3]: or at least you should, LOL \ No newline at end of file diff --git a/static/files/algorithms/paths/bf-to-astar/bf.hpp b/static/files/algorithms/paths/bf-to-astar/bf.hpp new file mode 100644 index 0000000..dbad150 --- /dev/null +++ b/static/files/algorithms/paths/bf-to-astar/bf.hpp @@ -0,0 +1,140 @@ +#ifndef _BF_HPP +#define _BF_HPP + +#include +#include +#include +#include + +#include "graph.hpp" + +const static std::vector DIRECTIONS = + std::vector{std::make_pair(0, 1), std::make_pair(0, -1), + std::make_pair(1, 0), std::make_pair(-1, 0)}; + +static auto _check_vertex(const graph& g, + std::vector>& distances, int v, + bool check_only = false) -> bool { + bool improvement_found = false; + + // unpack the vertex coordinates + int y = v / g.width(); + int x = v % g.width(); + + // skip the cells we cannot reach + if (distances[y][x] == graph::unreachable()) { + return false; + } + + // go through the neighbours + auto u = std::make_pair(x, y); + for (const auto& [dx, dy] : DIRECTIONS) { + auto v = std::make_pair(x + dx, y + dy); + auto cost = g.cost(u, v); + + // if we can move to the cell and it's better, relax¹ it + if (cost != graph::unreachable() && + distances[y][x] + cost < distances[y + dy][x + dx]) { + if (check_only) { + return true; + } + + distances[y + dy][x + dx] = distances[y][x] + cost; + improvement_found = true; + } + } + + return improvement_found; +} + +auto bf(const graph& g, const vertex_t& source, const vertex_t& destination) + -> int { + // ‹source› must be within the bounds + assert(g.has(source)); + + // ‹destination› must be within the bounds + assert(g.has(destination)); + + // we need to initialize the distances + std::vector> distances( + g.height(), std::vector(g.width(), graph::unreachable())); + + // ‹source› destination denotes the beginning where the cost is 0 + auto [sx, sy] = source; + distances[sy][sx] = 0; + + // now we need to improve the paths as long as possible + bool improvement_found; + do { + // reset the flag at the beginning + improvement_found = false; + + // go through all of the vertices + for (int v = g.height() * g.width() - 1; v >= 0; --v) { + improvement_found = _check_vertex(g, distances, v) || improvement_found; + } + } while (improvement_found); + + return distances[destination.second][destination.first]; +} + +auto bf_finite(const graph& g, const vertex_t& source, + const vertex_t& destination) -> int { + // ‹source› must be within the bounds + assert(g.has(source)); + + // ‹destination› must be within the bounds + assert(g.has(destination)); + + // we need to initialize the distances + std::vector> distances( + g.height(), std::vector(g.width(), graph::unreachable())); + + // ‹source› destination denotes the beginning where the cost is 0 + auto [sx, sy] = source; + distances[sy][sx] = 0; + + // now we only iterate as many times as cells that we have + for (int i = g.height() * g.width(); i > 0; --i) { + // go through all of the vertices + for (int v = g.height() * g.width() - 1; v >= 0; --v) { + _check_vertex(g, distances, v); + } + } + + return distances[destination.second][destination.first]; +} + +auto bellman_ford(const graph& g, const vertex_t& source) + -> std::vector> { + // ‹source› must be within the bounds + assert(g.has(source)); + + // we need to initialize the distances + std::vector> distances( + g.height(), std::vector(g.width(), graph::unreachable())); + + // ‹source› destination denotes the beginning where the cost is 0 + auto [sx, sy] = source; + distances[sy][sx] = 0; + + // now we only iterate as many times as cells that we have + for (int i = g.height() * g.width(); i > 0; --i) { + // go through all of the vertices + for (int v = g.height() * g.width() - 1; v >= 0; --v) { + _check_vertex(g, distances, v); + } + } + + // now we check for the negative loops + for (int v = g.height() * g.width() - 1; v >= 0; --v) { + if (_check_vertex(g, distances, v, true)) { + std::cerr << "[Bellman-Ford] Found a negative loop\n"; + break; + } + } + + return distances; +} + +#endif /* _BF_HPP */ diff --git a/static/files/algorithms/paths/bf-to-astar/main.cpp b/static/files/algorithms/paths/bf-to-astar/main.cpp index 9b02c28..0723e95 100644 --- a/static/files/algorithms/paths/bf-to-astar/main.cpp +++ b/static/files/algorithms/paths/bf-to-astar/main.cpp @@ -30,5 +30,12 @@ auto main() -> int { std::cout << "Vortex cost: " << g.vortex_cost() << "\n"; std::cout << "Graph:\n" << g; + // finding the distances from the bottom left corner to the 2 rows above + auto cost = bf_finite(g, std::make_pair(1, 9), std::make_pair(1, 7)); + std::cout << "[Finite BF] Cost: " << cost << "\n"; + + auto distances = bellman_ford(g, std::make_pair(1, 9)); + std::cout << "[Bellman-Ford] Cost: " << distances[7][1] << "\n"; + return 0; } diff --git a/static/img/algorithms/paths/bf-to-astar/bellman-ford-meme.jpg b/static/img/algorithms/paths/bf-to-astar/bellman-ford-meme.jpg new file mode 100644 index 0000000000000000000000000000000000000000..073cba924a999952ebbc2221d1efeddf6a7db076 GIT binary patch literal 37548 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 Ver~U#S{^OE@f Date: Mon, 1 Jan 2024 18:23:22 +0100 Subject: [PATCH 03/10] algorithms(bf-to-astar): add reference to the sources Signed-off-by: Matej Focko --- algorithms/11-paths/2024-01-01-bf-to-astar/index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/algorithms/11-paths/2024-01-01-bf-to-astar/index.md b/algorithms/11-paths/2024-01-01-bf-to-astar/index.md index 4b79929..803e316 100644 --- a/algorithms/11-paths/2024-01-01-bf-to-astar/index.md +++ b/algorithms/11-paths/2024-01-01-bf-to-astar/index.md @@ -162,7 +162,8 @@ std::ostream& operator<<(std::ostream& os, const graph& g) { :::info Source code -**TODO** link the sources +You can find all the source code referenced in this series +[here](pathname:///files/algorithms/paths/bf-to-astar.tar.gz). ::: From b048c6dc4f75895f7cac0bb04de61b0f068af900 Mon Sep 17 00:00:00 2001 From: Matej Focko Date: Mon, 1 Jan 2024 20:12:48 +0100 Subject: [PATCH 04/10] algorithms(bf-to-astar): add headers to satisfy compilation Signed-off-by: Matej Focko --- static/files/algorithms/paths/bf-to-astar/astar.hpp | 4 ++++ static/files/algorithms/paths/bf-to-astar/dijkstra.hpp | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 static/files/algorithms/paths/bf-to-astar/astar.hpp create mode 100644 static/files/algorithms/paths/bf-to-astar/dijkstra.hpp diff --git a/static/files/algorithms/paths/bf-to-astar/astar.hpp b/static/files/algorithms/paths/bf-to-astar/astar.hpp new file mode 100644 index 0000000..210e348 --- /dev/null +++ b/static/files/algorithms/paths/bf-to-astar/astar.hpp @@ -0,0 +1,4 @@ +#ifndef _ASTAR_HPP +#define _ASTAR_HPP + +#endif /* _ASTAR_HPP */ diff --git a/static/files/algorithms/paths/bf-to-astar/dijkstra.hpp b/static/files/algorithms/paths/bf-to-astar/dijkstra.hpp new file mode 100644 index 0000000..8d575b7 --- /dev/null +++ b/static/files/algorithms/paths/bf-to-astar/dijkstra.hpp @@ -0,0 +1,4 @@ +#ifndef _DIJKSTRA_HPP +#define _DIJKSTRA_HPP + +#endif /* _DIJKSTRA_HPP */ From a1f871ffb8568c18425c1fdeac1ec26fe31a2b38 Mon Sep 17 00:00:00 2001 From: Matej Focko Date: Mon, 1 Jan 2024 21:32:04 +0100 Subject: [PATCH 05/10] algorithms(bf-to-astar): mention time complexity in BF Signed-off-by: Matej Focko --- .../11-paths/2024-01-01-bf-to-astar/01-bf.md | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/algorithms/11-paths/2024-01-01-bf-to-astar/01-bf.md b/algorithms/11-paths/2024-01-01-bf-to-astar/01-bf.md index ca9d228..377036b 100644 --- a/algorithms/11-paths/2024-01-01-bf-to-astar/01-bf.md +++ b/algorithms/11-paths/2024-01-01-bf-to-astar/01-bf.md @@ -387,6 +387,27 @@ possibly look it up, if necessary. Also the correctness of the BF relies on the conclusion we've made when fixing the infinite-loop on our naïve BF solution. +## Time complexity + +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 + worst-case ordering of the relaxations which results in + $$ + \mathcal{O}(\vert V \vert \cdot \vert E \vert) + $$ + +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: + $$ + \mathcal{O}(\vert V \vert \cdot \vert E \vert) + $$ + +3. and finally the well-known Bellman-Ford's algorithm time complexity: + $$ + \mathcal{O}(\vert V \vert \cdot \vert E \vert) + $$ + ## Small refactor Since we are literally copy-pasting the body of the loops just for the sake of From 39213f1536466f1bda3a8fdfdb60e9f0b9bf431c Mon Sep 17 00:00:00 2001 From: Matej Focko Date: Tue, 2 Jan 2024 09:20:06 +0100 Subject: [PATCH 06/10] algorithms(bf-to-astar): normalize the output Signed-off-by: Matej Focko --- algorithms/11-paths/2024-01-01-bf-to-astar/01-bf.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/algorithms/11-paths/2024-01-01-bf-to-astar/01-bf.md b/algorithms/11-paths/2024-01-01-bf-to-astar/01-bf.md index 377036b..05bfb22 100644 --- a/algorithms/11-paths/2024-01-01-bf-to-astar/01-bf.md +++ b/algorithms/11-paths/2024-01-01-bf-to-astar/01-bf.md @@ -107,6 +107,8 @@ finding the path `u → x1 → … → xn → v` to subproblems _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: ``` +Normal cost: 1 +Vortex cost: 5 Graph: ############# #..#..*.*.**# @@ -129,6 +131,8 @@ 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 change their cost to `-1` then. Let's check what's the fastest path to the cell. ``` +Normal cost: 1 +Vortex cost: -1 Graph: ############# #..#..*.*.**# @@ -239,6 +243,8 @@ auto bf_finite(const graph& g, const vertex_t& source, And we get the following result: ``` +Normal cost: 1 +Vortex cost: -1 Graph: ############# #..#..*.*.**# From 6a857d8ccd79e2c232b2f949e228c428e32367da Mon Sep 17 00:00:00 2001 From: Matej Focko Date: Tue, 2 Jan 2024 09:20:43 +0100 Subject: [PATCH 07/10] algorithms(bf-to-astar): fix typos Signed-off-by: Matej Focko --- algorithms/11-paths/2024-01-01-bf-to-astar/01-bf.md | 6 +++--- algorithms/11-paths/2024-01-01-bf-to-astar/index.md | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/algorithms/11-paths/2024-01-01-bf-to-astar/01-bf.md b/algorithms/11-paths/2024-01-01-bf-to-astar/01-bf.md index 05bfb22..263e820 100644 --- a/algorithms/11-paths/2024-01-01-bf-to-astar/01-bf.md +++ b/algorithms/11-paths/2024-01-01-bf-to-astar/01-bf.md @@ -283,7 +283,7 @@ y = i / width ## Bellman-Ford If you have ever attended any Algorithms course that had path-finding in its -sylabus, you probably feel like you've seen the algorithm above before[^3]… And +syllabus, you probably feel like you've seen the algorithm above before[^3]… And yes, the first algorithm I have proposed is a very dumb version of the _Bellman-Ford_ algorithm, it's dumb, because it loops :wink: After our “looping” prevention we got to the point that is almost the _Bellman-Ford_ with the one @@ -406,12 +406,12 @@ Let's have a short look at the time complexities of the presented algorithms: 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: $$ - \mathcal{O}(\vert V \vert \cdot \vert E \vert) + \Theta(\vert V \vert \cdot \vert E \vert) $$ 3. and finally the well-known Bellman-Ford's algorithm time complexity: $$ - \mathcal{O}(\vert V \vert \cdot \vert E \vert) + \Theta(\vert V \vert \cdot \vert E \vert) $$ ## Small refactor diff --git a/algorithms/11-paths/2024-01-01-bf-to-astar/index.md b/algorithms/11-paths/2024-01-01-bf-to-astar/index.md index 803e316..b75a764 100644 --- a/algorithms/11-paths/2024-01-01-bf-to-astar/index.md +++ b/algorithms/11-paths/2024-01-01-bf-to-astar/index.md @@ -10,6 +10,7 @@ tags: - bellman ford - dynamic programming - dijkstra +- greedy - a star last_update: date: 2024-01-01 From 5164a31ce2c6856096c8922889f19f4d47e56471 Mon Sep 17 00:00:00 2001 From: Matej Focko Date: Wed, 3 Jan 2024 12:50:29 +0100 Subject: [PATCH 08/10] algorithms(bf-to-astar): add Dijkstra Signed-off-by: Matej Focko --- .../2024-01-01-bf-to-astar/02-dijkstra.md | 318 ++++++++++++++++++ .../algorithms/paths/bf-to-astar/dijkstra.hpp | 76 +++++ .../algorithms/paths/bf-to-astar/main.cpp | 3 + .../paths/bf-to-astar/dijkstra-meme.jpg | Bin 0 -> 86026 bytes 4 files changed, 397 insertions(+) create mode 100644 algorithms/11-paths/2024-01-01-bf-to-astar/02-dijkstra.md create mode 100644 static/img/algorithms/paths/bf-to-astar/dijkstra-meme.jpg diff --git a/algorithms/11-paths/2024-01-01-bf-to-astar/02-dijkstra.md b/algorithms/11-paths/2024-01-01-bf-to-astar/02-dijkstra.md new file mode 100644 index 0000000..1cd2edf --- /dev/null +++ b/algorithms/11-paths/2024-01-01-bf-to-astar/02-dijkstra.md @@ -0,0 +1,318 @@ +--- +id: dijkstra +slug: /paths/bf-to-astar/dijkstra +title: Dijkstra's algorithm +description: | + Moving from Bellman-Ford into the Dijsktra's algorithm. +tags: +- cpp +- dynamic programming +- greedy +- dijkstra +last_update: + date: 2024-01-03 +--- + +## Intro + +Let's rewind back to the small argument in the previous post about the fact that +we can safely bound the amount of iterations with relaxations being done. + +We have said that assuming the worst-case scenario (bad order of relaxations) we +**need** at most $\vert V \vert - 1$ iterations over all edges. We've used that +to our advantage to _bound_ the iterations instead of the `do-while` loop that +was a risk given the possibility of the infinite loop (when negative loops are +present in the graph). + +:::tip + +We could've possibly used both _boolean flag_ to denote that some relaxation has +happened and the upper bound of iterations, for some graphs that would result in +faster termination. + +Using only the upper bound we try to relax edges even though we can't. + +::: + +Now the question arises, could we leverage this somehow in a different way? What +if we used it to improve the algorithm instead of just bounding the iterations? +Would that be even possible? + +**Yes, it would!** And that's when _Dijkstra's algorithm_ comes in. + +## Dijkstra's algorithm + +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) + +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: +> 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 +> Amsterdam with my young fiancée, and tired, we sat down on the café terrace to +> drink a cup of coffee and I was just thinking about whether I could do this, +> and I then designed the algorithm for the shortest path. As I said, it was +> a twenty-minute invention. In fact, it was published in '59, three years +> later. The publication is still readable, it is, in fact, quite nice. One of +> the reasons that it is so nice was that I designed it without pencil and +> paper. I learned later that one of the advantages of designing without pencil +> and paper is that you are almost forced to avoid all avoidable complexities. +> Eventually, that algorithm became to my great amazement, one of the +> cornerstones of my fame. +> +> — Edsger Dijkstra, in an interview with Philip L. Frana, Communications of the +> ACM, 2001 + +:::caution Precondition + +As our own naïve algorithm, Dijkstra's algorithm has a precondition that places +a requirement of _no edges with negative weights_ in the graph. This +precondition is required because of the nature of the algorithm that requires +monotonically non-decreasing changes in the costs of shortest paths. + +::: + +## Short description + +Let's have a brief look at the pseudocode taken from the Wikipedia: +``` +function Dijkstra(Graph, source): + for each vertex v in Graph.Vertices: + dist[v] ← INFINITY + prev[v] ← UNDEFINED + add v to Q + dist[source] ← 0 + + while Q is not empty: + u ← vertex in Q with min dist[u] + remove u from Q + + for each neighbor v of u still in Q: + alt ← dist[u] + Graph.Edges(u, v) + if alt < dist[v]: + dist[v] ← alt + prev[v] ← u + + return dist[], prev[] +``` + +Dijkstra's algorithm works in such way that it always tries to find the shortest +paths from a vertex to which it already has a shortest path. This may result in +finding the shortest path to another vertex, or at least some path, till further +relaxation. + +Given that we need to **always** choose the _cheapest_ vertex, we can use a min +heap to our advantage which can further improve the time complexity of the +algorithm. + +## Used techniques + +This algorithm leverages the _dynamic programming_ technique that has already +been mentioned with regards to the _Bellman-Ford_ algorithm and also _greedy_ +technique. Let's talk about them both! + +_Dynamic programming_ technique comes from the fact that we are continuously +building on top of the shortest paths that we have found so far. We slowly build +the shortest paths from the given source vertex to all other vertices that are +reachable. + +_Greedy_ technique is utilized in such way that Dijkstra's algorithm always +improves the shortest paths from the vertex that is the closest, i.e. it tries +extending the shortest path to some vertex by appending an edge, such extended +path may (or may not) be the shortest path to another vertex. + +:::tip Greedy algorithms + +_Greedy algorithms_ are algorithms that choose the most optimal action +**at the moment**. + +::: + +The reason why the algorithm requires no edges with negative weights comes from +the fact that it's greedy. By laying the requirement of non-negative weights in +the graph we are guaranteed that at any given moment of processing outgoing +edges from a vertex, we already have a shortest path to the given vertex. This +means that either this is the shortest path, or there is some other vertex that +may have a higher cost, but the outgoing edge compensates for it. + +## Implementation + +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 +version with any ordering, in our case we need reverse ordering, because we need +the min heap. +```cpp +using pqueue_item_t = std::pair; +using pqueue_t = std::vector; + +auto pushq(pqueue_t& q, pqueue_item_t v) -> void { + q.push_back(v); + std::push_heap(q.begin(), q.end(), std::greater<>{}); +} + +auto popq(pqueue_t& q) -> std::optional { + if (q.empty()) { + return {}; + } + + std::pop_heap(q.begin(), q.end(), std::greater<>{}); + pqueue_item_t top = q.back(); + q.pop_back(); + + return std::make_optional(top); +} +``` + +And now we can finally move to the actual implementation of the Dijkstra's +algorithm: +```cpp +auto dijkstra(const graph& g, const vertex_t& source) + -> std::vector> { + // make sure that ‹source› exists + assert(g.has(source)); + + // initialize the distances + std::vector> distances( + g.height(), std::vector(g.width(), graph::unreachable())); + + // initialize the visited + std::vector> visited(g.height(), + std::vector(g.width(), false)); + + // ‹source› destination denotes the beginning where the cost is 0 + auto [sx, sy] = source; + distances[sy][sx] = 0; + + pqueue_t priority_queue{std::make_pair(0, source)}; + std::optional item{}; + while ((item = popq(priority_queue))) { + auto [cost, u] = *item; + auto [x, y] = u; + + // we have already found the shortest path + if (visited[y][x]) { + continue; + } + visited[y][x] = true; + + for (const auto& [dx, dy] : DIRECTIONS) { + auto v = std::make_pair(x + dx, y + dy); + auto cost = g.cost(u, v); + + // if we can move to the cell and it's better, relax¹ it and update queue + if (cost != graph::unreachable() && + distances[y][x] + cost < distances[y + dy][x + dx]) { + distances[y + dy][x + dx] = distances[y][x] + cost; + pushq(priority_queue, std::make_pair(distances[y + dy][x + dx], v)); + } + } + } + + return distances; +} +``` + +## Time complexity + +The time complexity of Dijkstra's algorithm differs based on the backing data +structure. + +The original implementation doesn't leverage the heap which results in +repetitive _look up_ of the “closest” vertex, hence we get the following +worst-case time complexity in the _Bachmann-Landau_ notation: +$$ +\Theta(\vert V \vert^2) +$$ + +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 +Fibonacci heap we can achieve the following amortized time complexity: +$$ +\mathcal{O}(\vert E \vert + \vert V \vert \cdot \log{\vert V \vert}) +$$ + +:::tip Fibonacci heap + +Fibonacci heap is known as the heap that provides $\Theta(1)$ **amortized** +insertion and $\mathcal{O}(\log{n})$ **amortized** removal of the top (either +min or max). + +::: + +## Running the Dijkstra + +Let's run our code: +``` +Normal cost: 1 +Vortex cost: 5 +Graph: +############# +#..#..*.*.**# +##***.....**# +#..########.# +#...###...#.# +#..#...##.#.# +#..#.*.#..#.# +#D...#....#.# +########*.*.# +#S..........# +############# +[Finite BF] Cost: 22 +[Bellman-Ford] Cost: 22 +[Dijkstra] Cost: 22 +``` + +OK, so it seems to be working just fine. Now the question arises: + +> What happens when we have negative weights in our graph? + +## Busting the myth about looping Dijkstra + +One of the very common misconception about Dijkstra's algorithm is that it loops +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 +negative loops. Let's run our code! Our first naïve approach was actually +looping: +``` +Normal cost: 1 +Vortex cost: -1 +Graph: +############# +#..#..*.*.**# +##***.....**# +#..########.# +#...###...#.# +#..#...##.#.# +#..#.*.#..#.# +#D...#....#.# +########*.*.# +#S..........# +############# +[Finite BF] Cost: -240 +[Bellman-Ford] Found a negative loop +[Bellman-Ford] Cost: -240 +[Dijkstra] Cost: 14 +``` + +Well, it definitely doesn't loop. How much does `14` make sense is a different +matter. + +:::info Variations + +There are multiple variations of the Dijkstra's algorithm. You **can** implement +it in such way that with negative weights or loops it loops infinitely, but it +can be countered. In our case we keep the track of the vertices that already got +a shortest path established via the `visited`, that's how even multiple entries +for one vertex in the heap are not an issue. + +::: + +## Summary + +Now we have an algorithm for finding the shortest path that is faster than our +original naïve brute-force or Bellman-Ford. However we need to keep in mind its +requirement of no negative weights for correct functioning. + +You can also see how we used our thought process of figuring out the worst-case +time complexity for the naïve or Bellman-Ford algorithm to improve the original +path-finding algorithms. diff --git a/static/files/algorithms/paths/bf-to-astar/dijkstra.hpp b/static/files/algorithms/paths/bf-to-astar/dijkstra.hpp index 8d575b7..2b23120 100644 --- a/static/files/algorithms/paths/bf-to-astar/dijkstra.hpp +++ b/static/files/algorithms/paths/bf-to-astar/dijkstra.hpp @@ -1,4 +1,80 @@ #ifndef _DIJKSTRA_HPP #define _DIJKSTRA_HPP +#include +#include +#include +#include +#include +#include + +#include "graph.hpp" + +namespace { +using pqueue_item_t = std::pair; +using pqueue_t = std::vector; + +auto pushq(pqueue_t& q, pqueue_item_t v) -> void { + q.push_back(v); + std::push_heap(q.begin(), q.end(), std::greater<>{}); +} + +auto popq(pqueue_t& q) -> std::optional { + if (q.empty()) { + return {}; + } + + std::pop_heap(q.begin(), q.end(), std::greater<>{}); + pqueue_item_t top = q.back(); + q.pop_back(); + + return std::make_optional(top); +} +} // namespace + +auto dijkstra(const graph& g, const vertex_t& source) + -> std::vector> { + // make sure that ‹source› exists + assert(g.has(source)); + + // initialize the distances + std::vector> distances( + g.height(), std::vector(g.width(), graph::unreachable())); + + // initialize the visited + std::vector> visited(g.height(), + std::vector(g.width(), false)); + + // ‹source› destination denotes the beginning where the cost is 0 + auto [sx, sy] = source; + distances[sy][sx] = 0; + + pqueue_t priority_queue{std::make_pair(0, source)}; + std::optional item{}; + while ((item = popq(priority_queue))) { + auto [cost, u] = *item; + auto [x, y] = u; + + // we have already found the shortest path + if (visited[y][x]) { + continue; + } + visited[y][x] = true; + + for (const auto& [dx, dy] : DIRECTIONS) { + auto v = std::make_pair(x + dx, y + dy); + auto cost = g.cost(u, v); + + // if we can move to the cell and it's better, relax¹ it and update queue + if (cost != graph::unreachable() && + distances[y][x] + cost < distances[y + dy][x + dx]) { + distances[y + dy][x + dx] = distances[y][x] + cost; + pushq(priority_queue, std::make_pair(distances[y + dy][x + dx], v)); + } + } + } + + return distances; +} + #endif /* _DIJKSTRA_HPP */ diff --git a/static/files/algorithms/paths/bf-to-astar/main.cpp b/static/files/algorithms/paths/bf-to-astar/main.cpp index 0723e95..aca946f 100644 --- a/static/files/algorithms/paths/bf-to-astar/main.cpp +++ b/static/files/algorithms/paths/bf-to-astar/main.cpp @@ -37,5 +37,8 @@ auto main() -> int { auto distances = bellman_ford(g, std::make_pair(1, 9)); std::cout << "[Bellman-Ford] Cost: " << distances[7][1] << "\n"; + distances = dijkstra(g, std::make_pair(1, 9)); + std::cout << "[Dijkstra] Cost: " << distances[7][1] << "\n"; + return 0; } diff --git a/static/img/algorithms/paths/bf-to-astar/dijkstra-meme.jpg b/static/img/algorithms/paths/bf-to-astar/dijkstra-meme.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ee512ee369273fdedf211be59acedbf92235d8c2 GIT binary patch literal 86026 zcmdqIWmsIzvM@S8Ai)ycCAeF#;O_43?(Q1gCAhmYxVr}T!7T(IG`MrgyYGAUId|{7 z&-4BLx@T5**Q!n*HwTJ0OlPu4D>q~7-*RH z?_oZ`A;H1H!os0_{DgpniH3!RiH3oJgHJ|?gG+*kf$^F4GYL5b6*Uz$Asr(fB_kOn z73Ciy5bxi=hx-7B3I~TuiGzVd`G1^V`vAxvAVE+)P!PlbNMr~oWQf-R04@Lm009jF z_-_dY0`eX7d#JaYIB#tHmHv4b0ut&S4D{;?009aD0Eq;J1OPyQKLd*R|B3Ry)zESm ztcbM~{bW6+`^P{)Z#xF7%gE#dnZ;1*NzU)9a-tM@XL`7?lB+vjKuo$z9$hzdj}7hL z*YqAsUjF}?IK04cJ=3meyv>p$iaEe=7hN6zC@rC1Lyh~yJhOF*{^JXc zHD$)ajN@O~9)OWThxvyxj{pF9B9 zWSXZQ7K@E5It~n7beNp9Dd9IxiyB$O;d@i*HY4=bdg@YVdmBUgta}A=&<8;UatM>0 zN)qkF4gS*yD7-GGQVs^{J4ZManbNMAD?vvVBc97LWAPDA+jPMW)F{r%;2R?uHKR@3!c^^WVPHRT?7` zlZhNU|5%jrE-Tw1?B*_Rs6hd>P|AZ1v@x(}A6TUfP%MbOqG@<%C_}V_!-p45m4T9E z7h^}3WHl^$OD*dZ6B%5`Q6fKkDl6BT431W7dIi|3p;Gt9nv69SD(TE^zauOI-wr_K zTiyhFJiP+6-T65~u#+Y^LlSqCmN8&4({@a<@$6a;#h+qf#N97^d+|8-ekmN-I)BHV zq@meZvu#|pc0|lgvz_#De$+LyN#fw0RkQG8Jkq?tEjlp7o%X(c_g-YiF-CSz+%&ZH2LKdP z5Oh7}L}6_D5?eIivVww$2s&EPLa;e{?jH#(6D$G%;HKdc0RS4wL?8g#96+teM2Q9f zsQRnEJrAMz2Ma<|wFn(eS+hk;GeY?f;2+8SHjx8nm9PL?EbZt$XlSywah5-(R=rRk zZ2#d_fSqaF*3@mlSJ3HW&S;Tv8FUQ3%b3YP{0Mna7S@$zNK5 zB}2EG#7gRHxyw?-IQ8YTm@EjkrY@SotY#vPAH!@9C%#Cv!PI?`)i$fZMa1Zj&ML`R z-EncF%s@dym3N`ueoW7{(#$ei)tQ>i53z_z+Z{G04H4>X8~N;bGV-;O9>djrG9S`y zbfCzj=Who7DlI?-J+xHH!*CSQ~V+?Kmp+VlHFmxQ$AbK@NhCEQ7#Yb`{GC68FK{eAh;|G^IA zYA}^104}iz1UC)PrqV=%rbYvrD=0|W0aV9AbBaW$}+vwdt>95S8F=Q+9;9C)H15 zcSUZyi-vfoypCrX-tFY8eh_qWcBP-tjLELm7Fhd#@bmvR8t_7?N8$??!EB;U9#ylc z%eu^lBebTHo)<4ULn0e5CB<|mFgNJ1r=o87$J~Ye$rA2qT{6Fhk}<|gkitL_+stbYx*w7FotXy^Vgl_C?FzO3wrC2qg_<66@HUxx}|c_iys4la;EKQKy`*bPkC zcjm<6NP+965M;_}LBcLp77o7#Vq8@dA7%bryh3c*O#}xZvm@}jfvBCL6Bk0bb@hoH zUjYiEZ1|s*P``xIUL3IyIq~=J>xhfaZ?8;j#EEC^j!Wb4EKB<4exX?oQ#-DeA)PzTf*ZoppRc zdp<&99UQzj3dM{Jo3?}e|3c;80N`i@BcgO|K;;Iz0S#r6{%Fby3Xo~s43qXqeQgJ&aqe7J;WZw&uS^`FDler_|z{w2z@_85&e(xm7(Q zF`%j!{hb=#HV<{y89o*-7XYWsf?F*QjSirahirf=SCB6fLC2`VO6TA~)ScY2v84Q? z#3QKxD*XSZ`_J+Xo(uTvz5j!;kRUG|Kdz`5a)wyPu_rMzMV+X~)D-=lqng}&cOV?8 zCQXs9fo;C&=RXPj|JM**369MhQANaDJ?5jKzZ0QPtyf57WQHx!b}Gn(fNP7356oS$ zdtLscwEj&V!W@83`PP6?Oe%u=rYYZQBUwtVshwLUSPoZ8T_j>t_MfF5rh9VfH))}h zt6ojiF7~nl?P%O`;UY?<4G%0|rsk}Uv6=@i0wPKwZ=0W2W~_O4iuUM}k#W)je(PkR z`vabOyyp*Aukpu^G$*tL#P}&F<2ud;rn={HK6^&NWu_xfMlG{fQng<^PPhIZ@xLqI zoG8eo-12~OxETe>;FRgX;SLSW1)vH6Z%U+hXOg6jdaZk1vwYuZr*avzV8j21Cp2`$ z-2Xup!b+=>6v!I#TMmjSHV)_Ho8s(Vw?4p9bSX~>YXp!BOf9xt!JH|P8Pmzuu8TZa zv=&THu!Yj7qlS`eJN_Z`PIGZecx z0VO^ZnxytAw#FjK*`_esqw!tdb#|ovjZR7@8$S(XL<*@q*y(G5!CPjTnMoIXcc<8o znH};$=O{ret4jHz_DFoCaLO!wnTkt#rlsQCyz9-; zb;=h_5v#)dT5Cr=-yC*Ky8L-O@x=2C(=c21=nq z<47_csNI)7jydY%5{Ks-H7E3quA0vB>SCbQhe8N+hTN$pV=>Wm5tUCv(e|RbH;XM` z(2(}w6;OOGWW`iA<~r9wqL-Kb(WW7J2Wdi2v~03$rc}h9f`yxi2eJ?S%T?Kg6GdRo zj%=wABPRwE^88hA>E*ieH=`@f%p(_?JiHiZ%goy^R*IP6e8^eC7bV=O$CiS(kCJgw zy8ay5eeRg6_RuY@>hoqr&Dmg81Z^00TD)D=kn+;}KrLI=A2$(DNI$0SxSi&PM2>b3 zw!Mp)Yojqm17uq*!K-gR2ZUX0bF{}o(ia+x=^-(vXAcmPHnCIIl=o!RF4q?;8E^fA zNeeo5=l!PZ{TZuqYu(&{6=}!&^;u^ckHorV{EjKMk(mDPUbfET=BlDfC*GJg8BL?p z_uqMPn46VKTgA#itZ^f}E@wv{;+C7{cYjJXTt$gpXEz2w{+Hhbbr59`BWW!^3l#tKj;2GP^_%7D6dvC@+y7$#gvo~;fgv_P|a!tEZ z)6-+ai$;AYT8he{?7ZvyB8e1u;YhFcmIb@rDw@@eM8fu_(8!{`;r#YPOp^=2VV0}t zkf)^^R(gzQyqZo8y#ZJP37}=)Jhb}bZK;!g*_7er*o4jN)Z;wtNAo-?L<85u1~OR| zP7j@`o7OA`mbZyC|G#Dw%9;Z5JMIe^!cC4+WXxE;J+u2845hKz>bRac^q&8hq z-+W&ZqAprUzbr81rXkNU5goMmYsdZ3{nP{4R?rsC%WZIHrk~bMN51o8@y?Urx_6Rs z2l*bNXkpJi`(E=TJfR@@(Pco)tm7&wy<*CQ|1ak+_$`!GkO%y!i}3%Yz99ede6=Ez zBBhS6c&eHX-sVlN6@;tLx=KV)7$|C|2`tm&(%0IZUeJkYrYlpSR87j$oDE(GBqT2M z<1(sbbBXmN$gn}bv*Y&Z@Z`2_+NR`3D>d4I5Riv>3ldbDK>ssH`>R*LU+b)AvfsIo zznfp%N~)c(2GDE^>DD6t&24noDv{C_u}hmK8Ter$bl4ujg4>Q|wV@d|B&8>=Asc2D zUMbt%jz|f$-yk^Mecfh0Nr$(J%v!p}BRfjEF9afc?&K3cz|a)Ua2E!~Lz9{kU9kwj zh;WYO@lAc1#Jq~#q-cuJG1_$i&_9kKU1NxC8G#jI0ytm94K@c@JxjO!H2bqHh4E|G zaQ2abXLP_f4Xe1H&?v|j0J&N!KfuDP@g|+OW|6L#(nn==IC}#+yJsil@SG2zp}m&h zA0|`vGVjcp9X*}r_iaXh-~6t|1-6U^%ec(IE;>h{uHa6)ypvjW^7vMj_K0`HsrB8d z%yp46>b_lk`P1>`E3{?u%}yd>RQ>;b7@3 zDyfmCEDcO@noEfB_d-lliJ5d1XW5`etYP*O7$Ryz^AS!82dYYjwPcHUpFNF+a1-Gq z+=*alCN!$rF;J-T=WM)x^WaB4+G_G;5 zzrjOL@^x)YG*aaY(iunT{E=}Vd;$~R#!JL!6q)97D|=(;hUyfay1Q^d5=+N+?0 zqZv_mGsT|0W_%sYg#@4**9z*Cte3yD<{?RmgcJ9LS2+ACZpR(B@lmO2>fxkIl=D9C zD8)y&OqrfPwyn|TMi0&Z%FAgnO|pq&xvdieI}xk+e;a|xt&|7jOr9Wx%J#OJ1j*>55wbkf{1YhrC~{@ZvPSFHmJ3-?j- z=mwo7X-zrWrXFlTy9sBoH@#qF_s)&PmD}jT5@(I)eDThf(&{&RB1_6CO!jkQaXXG5+1mW-AVDG*E`GX&CSq zCw3({g~l58dzw{gKYl-;Pc5!*KX2@wU?e`iC^i;pltSlXfN#p72Ci6PbxaKQ)-2JK;jYtDwe|hJWKVsWfhs(*o5_ea73I@5Tg2^UbnW`e znSo#}CnFIPYV~oq^J9q*TR&A@=XnrCVk>MR^eorA+DZy$F-}3lS0HrfGa+IY# zp!1IyRBQWWTi2yXI?3YgNC2_{KqH{}w!^Bze|d#-lJq`fHdr7w4xl2S38hj0HYJb^ z==p*D#KEB4Qb2jY?mq&gzZCG1U99D7ED~ zJ@oOB*VM_5E$JvR`^a`P^A8t)zYHoL0B>O;02(s)4JiL#aTf%C>09vl^9=@gQ}c-( za-QF(->T@jtS~Oaj&c-pu5v=lm(T z>ZWkugIcErnlU@Wt(ww+p3G#nrob#Pv0+z2B3nJNn%Jv11Hf>9K0kv4p~m{mYq-~t zz396J%wIF`2WpTJcX2w%WUWP9X$D9+A313`rmHV4=E z*1QRN!-y2i({2(%doX9n-sq~#-wvDPI5|C>ok214+q^bewF^{oLxeUH_{@*TO4mX6 zAkq%-FqD+A4!2XFP^q@5hL4hlnmic2Gvl7qbS8 zAi;0r+O!u3&5#h&8?h%g5dxlh9hp<~hCbL2b_TmpQBUC$SXii2W{PS(lZ<+3tXxz<1UUK_cwJrK2!%OB1o zuzEvRR+_=|V=2F4zlB|(AQ#ON1ynFN$6WOs^flCO>L8`;6L|MDI`VE~Mlz_M zl^f0=G@j=^uiWH@uLR{9Usw_&$0PNF*$Gn!j^d2xo!t2zMw+hCHGG-p$Tr?6_*AE5C!) zXXobv8I@c$8Cc<4B8O+!)l>6%O#F*q1td;#r6e zkqb<&?nm2chKZ?15?t7TUu1CX1duLTHq1K3<~6AxWyr}HsOi&)!{0iIOU;3{-z4c8 z(yEREWMhtKtC8(xp(k9F>o_f&f4$6>JF)v}c^?ORGBl)B|CI4m(sV<;rC5^|My6J1 z&27t6GhnkRst}ev1y`V~NX;W97o$!EQ&tYPioY-jw1A5n9-nD=I5DUC0Yy1QSZgJ;r4B%(8}S#;hI=`Ww{B%H-GBfXYnL;aT;p zN!!}T7$;891J1~_7(8p|b6lsMbFz%7A8-aUu7+g9tbHqEZ(=f=Gw^u`^%rI7)YieH zn5(Addml8?dNgdylxgCH7tp?xbb@uPzkY5eKj|#XtV`{rQh)f`L!=e@0x=reK2gPk zFDkky=iDG{Ja=~;(9;sN~>@_>~D zWK?QFGIIFj@W2t4!W3)`kJqJRIz*tUyP;_Wyw;&F&=#CN!aA-}{X9^^@2!3OSXhbz zedY4?3T1S!Eg(!OSGD{#hUPCZwC~zowvanu#I>#ca&P%qLiA0$xjT)Qr&S_+ zw}+|w@*P-f?INEunX9q7R=n`V&9IYCuK=pKW%)%4;;VHYo5n^^S|kKMq=ctj@pcnW zvCVu2B@otmbPS)jm@l)?ii6R)&IItZt*6YK1soMiE-aFeEmx<}VOd1ue*uDcgp#2X zrWmS9IJ3xxG6M1o$Ja8t8|wnV#?IH&+$Jn!RELEJ6Az_LB%)qS4$iaW%HH4*38OhO zlemMnq5OdCTcc7HloD_P$|B4cLkvm--{Qw6DFqk4dBu~O%>@lGlczcl$D^3ikhgDY z^CP>ozcgQdD^mk}N28fSW+1~XugCb2?gqafZOU^WN415Ro0$fQ=~bEGy@rcto43`y zZ`O87I)I{pNAVS4XEf72=I#=Ea<|!N{R+q^OOH8ncIf~qThSG9 zagY`P_JQfkQw#>lqZ~LsRN@-80)NkaXE1akju?qzi_xt!FxN%A0GmDlcyD zVQW@;>sn1Q!I_gk=ddl!kp5P@;;?L}JL!<>6=3yqN82*t>u{haX!%Z^Lq7H zQ^zZqL7q8I3M3?R(ekbMv^BD(Jkl~TWrS&oxP$8E$B*j-E_BxTqHOXv>saFZ5-;2H z^V!crf*a5v$|d*8etfB0uGETz7aX~8)5@HW5SgB$54KjZh#Q%Qa?2nQ7weTiYtm8a zfNHt@g+6>}wK%8_pqB`9ubBIazHM&5u$mH&3+xutW)A<f0fHk=%Pl_<|2$B8|^F)l(>wI$X_}71|g$Q1!6E{s5K_EgOc>OJBnx=EEu-DxK3B z4D${W$Q0?+BNn5KNe7chBs{hTm}l4da;(TB4*SEZc4#8LFP=Af-gwF2a6<<}Kx*Vl zSNo%t*Eu)^;>2Clv`lsaO5dmI7_ z6qOlmW@#MSwgp7nvVpo%V9u91f6cFbefrA1Q$;D`8m4FhE^rqH_Vy4xX772e@#Y2n zX1|u(uNKo#sCZI;I_e!Mz2R6UlC;U&@EWnvG?Cs@jYrRXMy>aQ>FP>5{xwOuBrJDI90N6 z75wAM%gChLXvg`baii1O&S%4}OPDIJ;N+q#O5%R%BF^JmWV>q4j{;7q_8v-`NbQlM_^T@5Pj%E|E9oAi0)I()R;%*fqjz!y$n- z)}uxC?TTpChV-ECm`l9z5Q#s!eTy2DtXHcrmLAYOY;-cBE-OmQAT?99*XKweCj9vI zP2$TxEY3OFH_~5qHw~@^fZ@e7qAhXp+Mse*>+bzjK$c4@1I)Od8>8B6^Zu$Vyz$`f z>!>g0D0~IiW|vm4fZoYGy5O|AUqxRYvKw!GB&F22I1Fg3-rsWuRrF^mff`C{+MI4| z4g5alpM%@Az$SV8r%8ubF-ekzytmJxg4vg)jeT518G8&el#2<)jziWbG0EXZLRki~ z_Y)hFb7VPo@w1WQ3WtN#KVnq|RPmd=dE;LJ^lmb9Yhc}YTbIy_EY4^QIjB1qOwOuH z?MkxJHn@TJrPo3qT|OS2R_KmAkuvaNX)&TZ-_1a^)f%d*j!oHuZ~ajw8h*I@5P|H>--8hzcRj| zP)9LfU3JbI8oL<)Bm9uwWz|vrnZ86GOQAk=<)a#rADbg2I?Pc`>o8wF`_oB`Scg1t z!3W!mR4W|jOed*AfUFgm@#2YGl1q^iI`qSMuA{89vnO3LFx-=qz4aP06 zQA}}Z54($mQGzd31mAwm0iuD$-gW$aLRZ(&;c+YFHB-Gh!IoM$DoGqfUM z38#T01iVTI#rxXat%p=;bQh{a`nal(WqYI9*U&E6!aQHoI`u@az*%6=w68}}Szjyc zj0Q92KsCQC*&3Y{9qOCw6x2p!U96>dw}lrOObjzhVf7dvlvOLpp)_zXR;T%xdiual z6(_3(F=6nxGYiCm#|i(CV<)&ceJ01icOqJHhOX$Pk{B zSvVYjfpTYC8(+!I@23lr=1{xtX1|Xc_^nK+1Wfp?yRq!+tEVS7!mf{oT<$NvlbaSj zR{ho-l3)q~A*;P2%+sft&3rAT;rnc8GNpoejKIjiR^;mujtiKrM>Gt;@ z+}YB>SA_!>F5Z55%V%FDjG6Lz4|ded)D9O4@5?4&Z`Z}TQDUm4;0CRkPdwX8r*9+B z`@WVN1Sjt+ygdA-BvY+?{#4x2EoS%BX#mpeyg8c$R$K3U!S+@z*MQt80fIDtziiH` zhEIV@UjYthL%$bhJ3rrfvS$CDC#=63Sa6g>qA&J)SvC)ksVwY+H)47ADu@;Rg$15? z1uVBo-NfKO$k$DuXmVv{dy=c4TwoEo@a7Ib4*M=JH$CFF}Zy3vq~ zIl78H&6U~}liNR&Q4X&VA~MM}fSp^_NoE;)rV60>rNMdxs(`_zWl`HGmFQe~SnX8?6Bp~!6y*X+PQBY`8GEw} zT*7*zvg8uvmAvV-S*Vh!6^#Vh@KH6;NXa;d#(Lugsp8y}=_I;`mLg91XHr23-W9g5HI#2~E7& zR#kg`Ohwu^vC;>RCP?*VXOYfgUQbc3Y1L1~sB{pr=WuHIA)9OaYOs6R)4_n{#veX1 z*QSVjB_T?rO;&=V(_(eB`{M8wu*OmGCFXSDyU6&V;$U6f@sq_Xz|q`<`i!q{%mHD3 zdiygjr}o?vLt`L*k08O_UkO+(|Q0-Z>kVWs^!tu53{slPjVyYq`mBWGaN%j#@%(4oRL zU#MT(Z;UEm7GPf8GO8MOdsB`wT#oEv^-2fVD?l$T>{({&5x}>#_70b;u-cCIo1V-M z;SsfAoAzTDw+oNW+I=6HvEwd%wEOWOY{oeajX<8pgZgP4zC0OXOaU7k8KKFL)aCIi zE0kFx-2mn&{45!lG!YfA(GDZw6HBmHWd^mLhP~g)En%W6qztxpv7UFvr*-ILk>o`^ ziJ+W~dIJ<0^OhziJf~5RkN4=2w^k^(b)kWQr)R5Ctzako_T;s-w>Jdp1wTN9*^@Wn z$c5ol{}m9*_2%7djxef{A#Beiu{-e`hYCEM8gsI%_P=2}_&u7jG|cN{O?f$Ux^fv` zIx!aM<4RW#N+|_KANtF>V7A}fBd@ahJ?hC9(NR{NSjNhbD{NYNuPTQ{TuY11v-4RT z^6eC)5#I2YJa@mjH`d^sl{T}R?4swDSHN%QoXd5kM>QGWx$ejPYl8K=;~6%j+yNZ5 zSwyoApY%wSLIM4VHn?DqDb=2mWoG1$^huay#^2Yz%qBzmp&6?_v09_)B7DvD%x5p_ z1%vwV=obJMXb#K4f@w@1WJh9>{%K5zrs|4-hNL67tC-e>jc<_$)Duie63>o+F5M$S#%RQaX}K zMS9N7Z2x=DKb>&-eX^w&7vuJi;W+q|9ro1a-%5nv3W@J=0H0K-_Aq~>HA_s>2b1zx zNi9>MUVShr$VM5eF!u^z>S3Nc@OWMjG4XqfFEaEH@q8~>jRATnIrpd=R5jh9?dCmQ zaeyX|3cH@^=w0SZQ(Ed^3l1{Qp!3jy-~9gEQP-P))L<~!031whg?}ENy*7-?BTW`0 zGulTf>hdeIgrx3`yO!dZzoww(TBp3COt~NUM8uHxBwn=87LFpy;w3Y8=~yQG{%{5g zi#YTZaAOoD*c(#8ee0hMQI3Y@jY37I&+knBjgfdn9m(_rCb`|e2zgwoS$gEZUgPF64dzzq+og*!O#E2SBp)08?(KBI^0z&00v=)o})5Z`Iyg{?tp zo>R=b?TwN0zY;V^^}R!7nfa6$Q^m`84mDLCIHs0Tvn?X=GG&`gH{MQQC!8=`^633( z#4xOJv_{HYz)|RZ(a)E)IV}n+n0Jzz{jB%!$sCZ3`8^FL3PukF>PfpOFP~(<+i2Z! z!%SW^kP_vIiN(s_bs#nydH8>U*mo*uHXyAo&?w@+X*ZS+l&Q(yd=?eRgG;7e&#!lh zo-+EvVD&|?T&ng2{t#x)($C&)C&-l{%3a%~L(KmWZGZbTIQYo#_SSFu?Ac}SN1Q-I zo}>)K+ZK@G`|5s-Z%9%?@X;+~++3I4wzShr-LYK=_SNsi_I=%U;V~$dH@|xdbQt`+ ze>XvI2d_z^yjoy%e%pWPc3u$Jz4Z1|S?U!!0>pX|5!B6`Y~2}kY)eVM3q^UsqTIl< zEyvkrh;sAY6Lf!!;!mX8fj(I;d%BkRRSjrrGo*?vm@#@9>K_`FYk-AW6kzn*pFXDa zW4UUfGnFVVD8R`4+$y!?PrQxt3+<4hbooUzI>IM>51!RGovB)fBF5LuKUmd?qB)XD zmn9|26SDfO^Ry&HKTfsgRa~9f>!}V=|DvL=SfDC`Ku>8%yBx;-^l;SB13}EH-A6=T zg|s$urqpStkQ+Co#Y<{!p*22QOjhm3gA!H`sa@g_C^K1_e4ULJU&Vh0+elCjJ6H^4 z>>;!FP_5K(sgTMP%xIg%?4&}Hkm_i%wpv@hB}@|aK2U}>8>=_$(aO0pxkhHc|4~xp zYHH0NvS|BG%T0~5OS*SqS9vg*LtzXRGFAPS10m1S`Y@6qnwD`4P_$$#y8{b^TKU*d zwNR+9NJ&a`lX$D7iDKB8u0GaN#Tw9Atul`*It~4-XM^!$1pO$lR!i#8zxIbA3Gc6n z-?Wco#_|gQ^s*}@Mcbj;u&GzT>_wn6wNZIlgX6RAdq$P+8Rd&WB6)cZn)flgRa^Bl zC@em|EgNoxSNb@6;@6NHnA-6-SPj!X~w51E*Nm2SVwqz$w zzvVNe12h97&?~3wOzH(27HN~2>eQ$#iE-o`<_ic{qr-$;?(`;<^%!HlOJEf{Y!7je*81a3-Y~3c`2u*DlHxF z4O>nrbvFsZQH*6;XV3Ev!ys;L#GESPoGFg>1FxWHr%j)w2M1@?WdEEm`C@f{!Ysl; zL@{shsSyVlu)?f3R(N|@t19GUUI;JS)nMh9-IkxEZCOrpZR?}jmE`O(t}}TcD>(SbUmB=2xo9A$I7ws7MiQuDPZr$X#Lau()l5 zXIp9&uVo#POP-`(3ltB!;_3BWemeKdd6KSG8M;<*OpaU}>IQH2@p0z2ish!M8&Ymn zD10sdA)VAgY^g+QmcOIEuwPi#%KNQKg7tbs`S$z&OhYPNeXDF z=OuwtzKnUtdp?mS6=`-3${9E;GA&==8`XG!zjzSF&e~t#kXj}B8AW(aY)g_Ue_z{W+C6r%?QHB74=RZb73H2ZEHe4B6{KGY!J7A+fHb;Cem?SqwUQHd~)jM`kg}Lv<_C;%_tkclrI$k zN)hOs3}}TMPC3arZRYf6H$PPUIqJJRtxNCc@U53%_WY$?fm8A+VlU9WX`D_;Zx&}t zAVTUMrq##(j@q4v_u<7Gmca5+$cKyD6*A5#+izhpvJrYW1KRk(D#D^daGY_9t9^>5 zeoCZlH;MN0Ai^;`AQ9_;?|s3n+f1jFWKT#~YO2oYT27hYho{r=2Zn{PiyoXU6-!x0 zF|pJa+Ega1560PI<$a}!1iXYNf$Hqi^zoMaFr!2epXSPmGHKM-o9cdB*U+k*n(f%zi66LNcqLsozPKl@LHJjPF-dC3$Fjbqg&1^7$`dRV6h(%57j>QMJfT{zJwG@+!gHs6En!eejg}aoSRR6z zB((027y%6(=nCqLJr+3BW7V3Ng)UZt_9w-}{?IaF0od;Ye8oc6#;CGPBC4OKqUKcg zla#7S{s2~1BLzU}#;1Q__P&be|g72w7&#^KX9e>FF_z#>pRN6Jn&qB)V=T@sbACj zKVrAG-ugSHbB)(A@(~QjD5SSJFf!XHn$R4Jq1p#SeHQ1bj~2`=fNDQr97x5??Ay;- zgU0y5SDc;+8LE>9HDN^0Y)=z~=dZL7dt~%9w0%7N_OHm_+f19un!Rm6&l;p4>#)rWBCwyp9jqyAWH!#BzB6JgIPt%Zoqudz1ol4dQEU zxr4jrjAoVjS8r_7iu0)tL`5B-M+Egx{u5H0DLkTnvoz?)9XvhHelRY5tp&m;MLB4y z9L&fNWa9K1=?c{th!E$2!u3BTT_a9SowymkI?-_cxo%t#!kilGHEfxQ@+`z+(_64QW09!p{I%hah+hx2rZQ zHPdDd>nNGldsBU|^k&zk>j0faDDqpx6*7n^T2Mlwb02BpfB4Q9m-_JftUq1r#tG~v zX_YS_`rulu2$_;gQeF!XL?^I}SSh_r`4%xahk$LWsJ8N3ZsW>kRU~6|6-IUf6QMVx zu`>`NM~hLnPC^ah&Dh{S6rofL|9X%33*MJKHG$@jdW3{_qB;HE(;8c83TM( zPPS~EG{2cYI|={|m(?*zMYM({5op~5(-WLtbALQ|vOGn0PYDC6>u>Cet2XK77np;M z(Vh=esbAC`LeODNAxPE%L1-=ij8C6f55f)s9jt}a=KZmk?3$`i&@>x4n| zUL2oKFemlN=WR~|?QoU{Qw5!Y~WxfI~MY%593-gOZ{f|Z-t`y~si*!-6TEDAs@am|KI&yWn zUg=~@K71w9aAU=xl2%8crliaeTq)oc;DHhSfw026G9A#u^0XiAcUfDuOT6N%yl)!! z4*!->&{HOYW_Qu_=o|;tR;Qcp%Q`ce%cFl-z3O6C)ta6ny#n|?LdK40ZYBZy=DCOl zND$Y^wR+??bVQI^pq=1XwzwQC_fty_q+iLON}uxGs^YiMO9kuVJwrX~E{mK`bW;?Z z*BFEQJi_Qhf+1ZQIdgPh{$LKN1mL5l*0HL*SL;_QKm3HX74jx8>{f|R4}RO zOQ5lOX#c;u|QUW}L0|!;e&Pli>tH*(xXW z8m%PIqE6Q^-@Hcb7eVfhNetI+#LycZ?Zn`-!# zhSDR`lN&tHhv1TZ<@clsG&g5Wmn-?kznx<|s3zoN8cCE^uk7BO%^7rRYQMck!_B$F z`&}*c@Xf#mU6vD8VaU?=V=FWY3lmx=3{S57`3m9`z@$gFb(Ismi#1h1nBq&)6Uk*i z=4_E!r~9^%+Nt`LzMla;J`9&g*$;RKl*;)CpUyjqV5hqpb8VIE-#4@qSi22WN2*>! z)Rs#dn1+p}+&IQopL)g-$bPpQRet>K=}@4cUuCDvOThynnO!#(>o;GrGoOa9%APPf zm4K5ia{UU3+jkF3ecLY*{i8~J#(|tng}2qZ zXQ(9J!Phz^r+IX95Yg)C!d83{Ju@Om!@Oo5tvM*8qDob>sufZCq)d?dPD^myuh4q` z6(B6}S&>dOF1eo9pcDtwtRr=hxRAm4#2_m5c>87fQ>X(ilmB@n; zd=`Vp0?e(F34F6T9ukpz^1KI)cwt)kz{Pwa`p{O>p5E4H8$z{ppk>=ntd0cX^{JEh zY8jp7cvb|Vk3;w875d*du8Z_M3>}M#%J4VR2UPj~AJ*P7D6Xb!8zs2AyA1B006~Mh zW^foJfx!vx?(Pr>Hs}nR!GZ-15ZomYJh(gLK65|M`<_#Es=j~URM+m=J>5NB-Mx3O zb@jDY%m1fOpp6IT>Ij(rUri!=p2V77uZ+x)?DgpffA$O$)b{wS^ynypuN0tNE+g7W zl6ujOh2I;hY^|aKj06M=IGoy6_Ucp0IjqhSNo)K65KGoxpZeYW)%&Qk?NEI#sJa+S}!o=8Hpu z;g;Mv-%*d6+V}N7zMc{a?EyADKeAPV1;&#`e)I_>$2g1-v^dg+aP`%Pwn49Om$JX8T=0?wtAL1fd z3L^7Pk0J%p?J|j9Ry(O!$GrMwhmSV;dJ40C%m{spa1}r!!;ff<(=C{XKoXZw z2E(Cq{qclD^ftm~y-Q471BKCqHj7zQF7q@yyjPU5VgXVOXPnTkKSQ7$4E$z2`Et`a zKVAbDPMvSP zTtEMEeP@Q)vxJv^O5;K^xs(WloK*-~aD1!9`3H&eA0(+M*m1TMCi{q5dXJ|5T=9|0 zg=5WSJU%FUny%#V-Bc#4Ny4%(qsOdtzikKgpar?r2fP$-9;7K!1a}4@VgG})-vv7F zP_K6(ZuDPZZrrrSAAG?sg{wb;=oA?qhaYx=rC)SnGYv@1XgU6v1W6fgqW`XK@RKyF z7gXKHKVA<$-u){@ z`~HI@Q}^eOj`wAu^&B+T3t_kZaGQ-i4kr57v{`+}Fgp5Lr zO2{N2tHUUWMj|AfTKpd}ID$7W^Xea@&{%oGNG8nx(?zDweiL{Ja&k`kYQ#32AujC6 zeWw}_4`dGz-wra>Y!Z_)B?js5%@f;#1{26*2lC>Jz-~@e=6>SXNqs(Z+ZC4vk0+v< z5#Mntd9uU|c&sw7zKG3`XzwRi0IQ;pNY2G^xOiex3&j(3j0J6%O+XVgqf1TDAiSUJ zJoSE8V`ypd2@O-3=l)Fcgt3wGs)Vtse5!bQT<^55&i9?SSTB0GH>D^13xHi2#{(|~ zCpVmPhhEf9CTvwiY+q0>9{=KeO?3 zr&X6E`@4Dw=}u~_+I{q*GdAJV82Ua3S`7-||3WbE7Pm$e!8FaT5F~$QGqxkVr!%OU z`}2};e2y19uU4eg{o1Z(vnYO;x@+RgrBcY8O!vlr2Lc%x2^}#Cuh9N?Adrbr5CnZD z0Y+IJK{-?*tI+=r1oD3eB2tp46WTO({%^SK!Qp8d`~L+5+VrtBo&yo;tNo1qEFaP* z;zyf)s|v~!Tu&4Aap=hRCx|Et_HiVVh|P%fP7^)XPCWB;o{59TW;ysu+^=mhVm15-E2#>kOx4Ucy;jF!_Jj!zDXJ7bUC;xXQ z>jG6bDK2tq)+oqyvqG;}xn_v~J7!!=Kvxw~`Dl zpP^xXw5TS+%VR>N^A40G5GPhZp8dd1$vUhyp{xJS%^{&bGAgzsr!}aylK9Uqku#In zBgGGeKOy0NH$Opp5W}m6Fu_1L0z-KF!azeuMgNaE1_}}pDlwBFiJUGWv$Z=ZBa2X2 zBbtD)yhmDztezFzK6ZdH_Qr?={wK9)8U+*h_{+s3&(PYt8&`$?KrtY{28WCtgzb2NXO!G z;!TFTCUS^4JC`vA7R$FUQV3mrx*XH-xz*8SZ|spf5Fya4N*4d&xiC*>?~c0mw&g4 z!&O|FqkUm~4*4~y+xghiQB;_=*$_SRdSNwQ_#(IIP&U}WL-0BQ>@3!cNlRKr@Tu`Ah?VicN6-AI zF0b@6sc);&eD~m#=ZcBS0oUC(3FBv`+7o4O0c6qEr^_KQWj=nQHTpw#E<;nMAW@0@ z=;>&JfSFd^;E@cXUz5e`T3$t}qy!TAl%>|zZm#|VEJ1ZP4@UG%XM1e(VHP2`q|>mN zhQFC3Pjw$d1R1KTeF^$gVPP(rmQ*Yn`@!-BcrkXj3f8ktCU%Wvt;fu<*p9}NeNU!K+Qm3^I``45QH zs*A}*PcD4!NsK}J?UJ(~0Z+FbK!NkSEqA}76&J61g+ao_?zO5%KB3XTL3ek3ca>fB zp(IPb(#I93B-xeQF7hJ$%wmlA_cKo;v?u>sgSWRq%A-rb#4^kWs0fAWP zmsb`Oc0c{-2yE1uPGRn#m0QSGq1IeIeT_F%Lnsnia%DZZ2AW(m4u4qqqIZN(1>|HY zbh}`Q;*|JwBP<$9lVQANH8$s+%pKL<&|}h8CGcHmXrb#lC`!|ih!|UXowBL655)IBpp(p%agLQ9%EfP|C zWL4nRFB~f*C0+a_s}V4m@x>Q=%;y6W8d6Cj;2Fz5NGnl8egtdiIfOgPR61U}if%`P zf4s@W*zB#bKue>oi98Rut9HPF?UB%)Qi_c{NLu zDuqp<((Z`n@57uYibD!t$0EIDN2};y3V0`m_4qZ;ijj+n zPGSE2o5NjJtrt-KhhVSNpOIHBEfC)ywvt5e^A#~FCB_B~ZQu{n>H1cj<`Av>HhQw- zf=+Qd?p2kN9-l&6;+mFT`k>ald4H6NaCZa2m1}By_%*4p3M7STrg@-$xUWf-`2u|# zV!}xwb9!~cB9Jb3hsHxo*w`}kxZqH3U`aj7YIwE3`o}nAa#oE9qfPAW68hN?B;Lw} z=yg3lyZ{GMarGnPcQrIt1QO9=i5cw4D+{u|k*+yee1Nu1I7}U2OUnE^9^Jde27)Xm z>IrwhugpV{B0AT>#u#aa2h4eI!(?)er=JWL!1Swt*37@y+t=)FQ8X# ziWkrXuIMGpFJSqiTQ@LXbcp`~`?lhaRgE}X(}f(JSMf!I9imx7U+%g9#=gluty~jB zW6r9zA5alLz4kgkZf7K*ZpbnnF!~61Pfr$y{~C<3&K~qpUb0mT^{TcIX)S-0)#w6x z(O|nqw0pGKGq#NT?X}a_WFIC+h9vQAvX~z3l!ZnY65+SK&xfiPyaRS~W#ABM`5vpc zhnv#0wRAYCcv9dKVol8>g6~O$H0yIs%D9fpKJL*2(`Z&@Tj74%a{g?vuc;`Hal>B8cNyv zn-!bc7i|`~*V0;zJ5`o?yEqCh?eBwnRpnFa;s-hJ0{yZn2l8HxJG<&e-Zdo{bOwmD z@CN5Q>*1?}EHqMXl#51bC3KDRSSnkS3y4g@?bvR~AKo*N*Rr_@)yn~YYJB$ve8frn|}{GK8BO1vGSflp+Ea^@`St1m9+pF2|-hT}h(9eq#clkOfn%%o!k)a3bnJA!1IIFfP z*#qOI)l{9H>;u8Bna_tGGO^ff(f>jErmq0$Pw)P8PgU=xS*7&l16}LE1XKjMyk4bf zNMK=Xp=_YHD%Y!J#RK|Hk*@wXzr2|gNX)3^^P#xp=ho*#9M7JK?3eeudOw&_;y|vh zDDTrLm&2&jbgWoTYk^<5u0IxE=gBsD@OtWvF^&S@QCb@+@^(}yK0{^60HUwv$9Zpb z=+J~-qiJn5tWi$C>N-&exDC}Kpo$YcEUZl?3157};r^<`5*h;+pN3-x=4jEtZ5?g` zZUw50JJ`exJ(kHR4`-V@>#1nfXO~gc7cfOXMjTt{^+ExSq&onwHANp7$PCXslc$v? z)!3F-+1KRhz;d=dsJ8`j)CnVKV1+8UAI~xml_ias+V_z2g-iEYJL`d3$Wg7@68+YM zOX@xQ#<(0Ws$?SSr^Qxpeb*K2G+JvPHN30b?j^J6X&B2TsdDe2V|u4NTAt|AH+S&@fO)w_k@YPd6f4eTtJ-Wf?rtvRdZ8Me zZ>`2`Zv%b8n$PcCldSDDYa%zG}?E2Zz3HNqP(b$V=yV+&I_mGkc(nnPQF3aJmX4uxaD(QCx5OXnNArk z>u!w=tUbUL@$WXr80w-Sd=@v7T%h;WvQxLbf-;<@N_z<Xc0a zL$3OAhQaWR?o)2GFdeFI!W&`Up)4&J%doTT$ z)iQ(+2EN$~cw{iS>@midP45abDVM1lavRGP^5HVbA+lgnusZkRfFv+t0OMO#B=!;ywznf!9Wh#}bB^6hdTg zkM`vjHG~V6K2-^_bfIvd&*yWa|8@6NqSQHuoq1bd04xMur)Ev8T=28vVguH1Y_RTN zFOFE~{V!mwU*$pUY<<$+|?_)9MP_TNwU?up_?;Jm%OL|%wuig>N46IDs2|y zhUo;}P}RvDg`Uq_Co zu7MpJRvn#y`a@D@%TDjCPvG{1YyTjLl@@MlKw3{2uS$KDE%!u^OBCNa4m*y2Z%t#= znq3HeBIY5b{vW502 z`tcJSU}J{_)&?*&OW#4y@Ny5GoXw;$2Iw3~06v096ef*x`jm21aPfuG??W!7y(!dK zlub$BunXntXpzSPZjMoiC=r!Px%>3wuFu;eCBdP4y#C0tkbI8IrV|^8kaUe=lG|^y z(s;F!%u_te2v_EhvCN7XA=K@$DScoOLF3250R0*A2hE|DT0HJmC9Fdc(ISp-{UEOs zfuDRao9d+UKj4U-e)fxRixK=A~7MmhaN2MsZvLG5va0p&fs@`4F zv&F51+5g_?O?RjW6Ka31-U;KG?6Un#EX5s_NqGXM?m=FEHW@+{er)iQhKsWJ*AbNV zv-)a~W}?Y?`#=18Wb%2K)Q^9o(wdzMHeY6=EFuxxV9CENIUc-;KcVPrNGHTLCHuMR zS~oUk<-UD%&>i7yD#e)PMMMEGt*`74yqbx9*W~B*YWd5VG|yai`HSS-qg~{Zlpbaex3bfw zowwo#^6t0E#r1{mkQTdcun|jwQ`~79Fnir-S~=-ZFd=!WeYV}1OI3Wde73U~nR-Ph z0?mG4!<^qS(Z-rhNcN8V@?p;Hg|!m)c%f@q^UK@kTa8~lKRejJqD~(mSzZjLL zc4cgt$T+|~Ip4tUJ9>WzmnTwZ_LN>?pq;*03@;oiKonv(6;S5b*Zi>EzHZgelf?yx zyk#Z(hpuO=7`qv=X0F|q=0{>L;XFEtvxR|GEB1cnC?^CaawCC4`JO}LC&f2Ix&@5m9*epHwO`G(Zy7dXVkAWjw)=f1#* zl|=E(;i8&ZqMoE{*U>f+O-B}`0v>ZIBmGn7*;0B<+S1R!^r~v-(MZhd@vg(7!gsQH z^S~u@sah0M%TgMSn34Pwk)DpRBJ>m$Mo3jJ+=K<86&r<`gm)_%#eQ>wBNcW(Ls)=N zb+ENn@Z0!*kUF-y5q7CP&2tE7vk`dB_VewAq!Eb6mZ_V(np)Q@#gx7k`ZkoD<#Db& z2F=&~prpT6qwI8JAO>WzQx9g{%)r)ySSDzza|paI(l zf@+t`!7|atPT)h0Y%rJmS$SntOjM9-S{h>dJ$Ce>ol$iGMfQifu?)@1r9H zX8EHynm=TaqsNYHdiQ&_o*X-i0ni(@RO4o@G)~IzP3rPjpd6 z|1QswHpFLRT!hz~pU{m@?k-Ct>>+Rca%dzDrxqe}XsGsJW$M}3S4%t8l_e8N=QJDs zQiw%ro;5U|y*@SY>7s&!Nzdf*!603cft-qep-e-XA*QZkv1(qY)vWtdjURUU=rZ-= ztrUyWn%`ns3vD+2$@dw^5U3xEMi!5ImsN8dMC$0Vrr&!kcLScyjK0A2&Q4vZIRL!GIY#jwjH*d=md~v($|Vh(F7idQiRE9&&TK^1^j!dwYU5r zE^)@q68*a9FH-6}%D+wv^yJ&XzztbPj*)gnqppIbLnQlkGgM5Q#e-Wp#=JtRR}Am#?}F~#toRAnY`t&+U%jrC$fAo+-~Y=KOl3m zeZKs&)}TR5OBxq?FNOl;JEBCQ!A0&>&MN7~0W>zPND1BTTy{3B3Pw zJe54ILKmN|5!SN|O5veF_ck)7}GK0d1qz-z~I}FPb2*%P6x0V|-?*U;HNCV2A_E zZ_#%ZQ3i-T6KR(zxEtj7^N^;5xm+c)#Tl=2P$@DDa&zSP5x1z{{Ef4TfV6q8%-uxP z^~qF(V!2iA+=5#_)6!*aIPbz&@#7Zjmm4f`t}yK*jogn=pv$mhI;gifmFEriH7$KS z);By?or$(^TVCh(sev&YG2IK1rJpX`Q3Z|}xJ?(&3FU_`f|UO zx(x&nA^{=6E-+E7Lr`?XX?hHtF>^gjW3u9#Kd@!n^`(g|VFSB8@XuCx<^_7Y-|R?i zgCLM(QO)V6O(H-fm%JXU;CMHf1x5;fb zvuRzB^h)xXiwdK&hTup$vG7OgT_{Ty56?2t)H1HhN@#D()G~=@pU{+_`+eDk9%Tkh z$*qYBSg6gV;pf@V*aN1Al$*`VvHPR)3-vpA~9 z^guIi7=$anvYn+GZS30syz7Bsl|CuLdzkcsSft9;&rIf0*gLUE69zVS#p~s_jzWS9 z?}s${7U16mlBO66MNS_`14`$tGxVv-u7hb5yON@mxkhP8U`j`KdD;g$zqd~nFz3!J z!f1$)JUyMO-4d6xl6$hR(`4U5zp@T)zteJZa@fu>Nd9hG*RvtOg7p5IKbwk;DN5bk zNQB398ti%>*c)_eS*YZPg=TH;nmzX7U^fVQ`-Q2Cd6(!yWXe@%UFv(int#IWyRu(s zMx}$WZv*)SemO=CI|6`@n-0O|+CB3j&uD zS?HED59=itW0z38XK62xc==dq3>j(07Htz$qDn*Z8q+FJQSLI{yaXy<493z^afi(jt`{vVjsDTHzgIki> zt8O zikAqux>-mu52{NUO$FD^EYhw6!`USYHOU7-o^QXRX2AUEg~ogOA*NXIP4w@X+j1X# z7k2BSbCQUq&FhV5yj7(j*e|PIU0g%5pTG%oZhutP*Txm&`m<&hyHw6js{nIL*}9n@ zr6ygd(4#U57GQjwYJr-#0*SWpB=Hgua>-wiNYy@NltUOATr{dks9kTlet&6NgD<*Z zT9C~Wu$d-4 z0o!M#wwWGs*Y(AFMm93@W*PyD%s8+hg{!3ZCOt=9s>zPuKS-y4A_q)mRlDrtdR>2s z_1KKFcff~Y3HcP0IJ5VjOtRnIY0bw*E9&;QlV9D)HzR5FUj5|>GgsimabIZuz0>$I zYmsoB9dM9Uqcj+lBiO~+hr}%2K>ePCIxLlF^l{FWV7ai}Nc;z4>J(F-?w`g}=5#0- zh;`O3)sBWR>229rP$n*g!K~M5w^`5pfh?1`Nh0UfjJzKbi5FKg(NphtXWEh%EZ|NE zD-*t~;kahj8sLB(Hp7;g_(K3wE)rlIe6J_$^4(P@F!<9(q5;LO`{M%doX=WnPob)q zU}U1`sG}gI3sQ>}cl3FtO@$9drHV2!r|26vR0f$f@n2kW5Se>-&Cg)!81il1s0w5H zJXG!s43i5_{8Pifzfd9A*QtooGwE+hF8;s3R)R))YDrQ%SrV! z4KFT^GJVp9GQ+z|87glJ@cxPl0S=G1dihO$5Pa2JF=BFmbuT{i6hgdiaJITZcH8tf za}7;gGDGQ2j!@yCeg6P<2@+oicD~CW5RbBgzvaL@i+sz6jY7%_3iC%jrRhK6y&F1Q zx2g9O(8dIgUDX4m&jqGx*kZ_YON|BbfuD}CV$FJ;H;u!EiJr!_&a2US;)~vh8j*{x zea7Suq5fCBM@WZB*)1l^U_02q5w|TY{r|iBpTz%-P8WZ{vb#@$R{oQ_`@{*}v@mAM z>jc01-yHv~z6o>oBM63E7_FI*^&-7nz$Y)jFR48 zT_*E1Q@kS^HaL%-si(Uxi}H2DY%^lq?cJMY)M$4-uM^9HZ-4a0dbe+blNM zq;125iko7@9@8u3G-E%Ynq?tQrJ^j-tOic z7%;<)fT%K5xGkgI=;}r_YpsBdY9%SFx&xN;^kn%BwTK0f5 z)N#{4=qWxchS-0JG};HR=ZmmqPoZcIS(lGj~Gep$NYE9aPB=XC&Whtz;V za10l57XUqBD38ZUIZ5@e%g{eaMBV0jHvQ1q6L)70)%eX)d%D5(8-k=s>p^SHku|*U zb@W_}NATFjs-2Wy*6zz0cv0IRf(`QZ(m+!swDGhz4nX(k(t>9N#Wd&$tY)0%!oZGv z=|FGGUavhmbk6FSHgFJ0jEt^zRNAXPW)y+x1OT2jFY70ICPE+#LtHY#Iu8Ve;yKq) z<;}leLnx~{Cnrh}3ijmFmO-d*EO z&@v2$PnG)P3CZz{gl1;Gj1%BV?CP9XHj1D>=CQ1A3I#D>=w_KjZ#R z9%*H))eXNmN}0VXlBNXsdRFMDXQQHy!4B^Gxg{;;xqntHvZ$qlns_)c0#m4+bbBz{ zWw43+Tv}VzJ`5^pUWNRf^%wD=EVKH#uWs;K)3=7eo5%iKVX(J>bhq%QK&Y1+}$OZ?9aA9{_j+!xi)Y1*y6?m$7Em}SF!JV3Sm1y{kMsE z7EQN09^QJ1*H+rZzn?kXc8ZvhwX>9^-gC5^m!1nU(ts=P;>_zfHde%qN^?IR+r2$< zc65r&ZV4|V=EUs)AN~xeuJV;l@!Vtaimmmv~B_avKHyEBY{0ez2t2S zRZa@Z4;kfFnY|>dDEwmo<7 z8>V`zLYBj|dr4OAo^l!gB=QeZroo$jf8MCxRsUO8bjv6Oa#So<46}$Vr5Lwj>h@@% zz?xAcda`w$z`>yWV0maS(e{7-2>unuoEVa6RMnFTBU0{tzg)S)wM@P)-#sp~OIE{q zk3DS=i1u%ZZ^Z;>rBFTz!%(=-9D}Ja2OG3>L(gn;$oXMPrPq+=)swVpro=UZiG`1# zois%0oVX)AFU4+0HFYLg&C@za?sXNKJkCmdU!%5^k(^oK`Dw|Pw5gUD{l-Y~?ZTSh z>@bfXmDfx@S@MHsa>v80k20>9o@@QCS_%>NcqX{M5SdXTouaF~$`ywN3#q}Gx}2Er zy#k1vTiK2W{qbRzQC}o#ylxfy71M2G8feudl(0xFFv@izf7H`GljzHJwQ=SMbxV2o zJPQv+5)J2@>U5RMR6ZWp$_qXt0C0_Hw9Mgy{SOzk&=PvjGW&m!KqWsW7SngblTQm4 z9PI2f1jHMbJSuaY~QGMH6^4Z(THZkU#7J8m^&s8gWubkHZ^z5rlQ)!iF%LVwFWGxrHK_SM1pKPlQjs!l896HJanDY7~ zrsDIZhF54Z^w>N%oaStMe&M+Owa;OGT^? zycc1V_UmhdM6_YC2eN9Lsz-7rig+{eRHR}HWrXnPy!wgc4=8*weGy;dNsm8L0kCPn ziB{Cq= zi>^7zrNgFmyDrL4)C9G~dFGBy#P%Y6r&mVE=R6a)T~TlZS2BoxPto<@Rrx8+`29Ld z78UA`QIXktH4qT9m+taY31`;p5B9reD!D>lq?@)zwiL{nqkUq|vd$;!MFv!Giha!u zyz}}c)S<$DuCOJ zyKh%LOJ4Hwe7#Zl*XyWZJ8LA>$nRXNb{3-Y8jk^<#V5t`22L zbQL08nH)SCux^6n*z4diRG7}jlS%Sh>|Q$SXC{FL2^oYyx7osa<8N!SkfiPlsU9_2 zDM1<>yVI3I+o(`KzF)i=uZ3{Bk_~403*RN+kT$Rm{l1*}A_d~ad^^2xjVmBx_Ii@r z?7FI8`aUkh>dk>(F-Awx;idRou)##*SSHX;jDq&MSRdU`Bb3P5;|A`|8n!OX^v2(z zB8w_ph{c{yKmEG;QrUsB5$8RyVj7cf%+c{3ChJc_vJM=Rd8yz;TW>}d2JelT+^@Dj zPDwZJ9LfY}Vf?>=f+vQ>4#1alV)|iz)Om6)?aEAP+2q$pqDhN&Knx0J-YZamd zB6nIglVN$cEWM-*5pXaNN@$IeO5kwn;xrqrb#JV@{)1JKnDslhE$;{*R4cAV>qo{9 zxAsL1bMy5C9QfG!T$-O^PGx>zA`28G$ zLPWRfy3+Gk?!^(GfnerSi}@{7XG>r#1{FWLuR(9vCzpx&hGw(TNSRQ6llxu z;#C%Ze~2{v+Qa&S7=;PxsO3>_Gl-vp1+5*Y@<{za_|5Mz_sLEDug$9cMhyaOZ-Pmi z-Q%d_Cf+|95G#uu*WL0w3G}EvY7BT3aV1>OG;pE|sTv*wDZ12?u18S6f7J;n(;v-d ziA@*vm2>JgypD9{iF~&}eOA2Qo%pUuAs7GU)Tpr$dP$p(gtfl1KrEd2IZ@vHX@hG( znI(rvJ(Hvh2E3rnwzI?WbTP&DUE3mS+S?qde~W-=9aj3DUw_(f7Y$Poi8oO~jaY9j z1X)j$r0HZiMvaR<3>wQ++ZueR+7J8( z2JAiyXsn4N*c8cwDISlPGnJpW?1+^#zpY?fTpvFRcqkWR40WgYv1JHHG#jzGi^PvG zHDG8C+OsK8>fUPBeBqjCa!1!+XI?BH7>vt^MVgiI34-aTe>h#`h+X2fS~s>O7Ajco z#N&}t-jW?GKgv#=Y9|_Dj5V#4G+2|CsK?(@=rao1lle`1VOc<2qN>3>o+P2#ZgwInq|VC2*ns0FofhKR>>I zmnfjY-dt@5n}pG-N4T#;waW@r1=}a9s%$eA2xB8`7ez$|O$Ei_*Y&CtTXticjsuqB zt>XSs8F9Wz3nzLTaVL0_{w0|kBFtmEdX!fdNS{&_Te;7yAo%j>5x%CG1 zJL=O1ADQ7?RRbu-S?ka1xc6}tlaizB$xivf_ftRP^Vb?Y5HcLmmHo`ErRl z4Lr1^7~{PPHlz>Sj&4CSSus${gVB&v&&t)~B~6+i3B;MpHhtb z_XBTRH~Sx?F;s$BI97B(gd2bH{d;`P9uwO)6Cf;B_)J;mh7dBEd%z<4Z;zfVb?}pd zrzSxNr}&1AHc>mS9N^49P04xy53?Vz6azWYD9Eh}NIFbp%X}o$9QH4w z^JoW5vA<@tn>yP!VRd~GzAqn2;k#kK3o_1o^0IoC9gu?8OG0Ef>5@A7IkQC^8w6p-s?rUF^*f)E6^Y7J#YCPcHC~M+BNeIAB)qJjj z7mRl(C*`4-<_WILjQag4Uct`OF{?OUDG^yXS!o`b$5o_4U})j_;I+ym3$f5gOwrsj zrmk4$BjWLa^jS2NiURD9yHQ(i!oa09o#fZHS+U)pqp(QH=E4a~GGb!<$YuNnzg9IV zuzXF(eYO8C79d!6R$R@LKmbV0?t;Os0Wq;I1El)_&Kl5YPQs-KJ=GjB-2m(`yqBgu zjMOS>j-9aqyN0Jneakr1IOcGXuGcXHfLfyFrd0*aK2=tILMHU=#i59*RvkZm8Dtx3 z!S_Kt9DvjjdON;u=CbkVtalYZG~v<)_FQtf`uvjDbt3UjyzX-Eh^Y#6@-6vIFd~>z zdvGgq@MbEu6##ll?Uyu;5>tQ2#6HZU=di%Uo839AAb72a37O5w!X?}I^o!>s^n8S5 zDHShikoR{Q(aQqZ(HRax9sP|i#pSOG70B7wTvO+iUOB!qm!e1OwY+XTM!k-Mg#o$vUA4nu!d@lo$Ab zV{nPpmKAVJ!A5jBaTIXuKYqSOzFTR<=tFYgU<1sRNKTwFCe+Zcw%2}IbUqoHT<6w1 zSwhh~B1i@{1f=QT5_V~0Oa?zplsE~=u)w5BE{mD9x?if&VKOTH%?c!6Kf zsud*AC{5Ra=|K%U{Hw?tk@#Qhtg_sWod9Bph0P|O>TRx-Ee&VO4K;^f{$g5miEmDY#ELHin7G1Iaz#j1kDK~ zFs;&racW&;z~9U^ti(mFnn)%UDhrWC8Jd(>D44!W8?x!zef-groP|%X;!z{;^DlBm z-!KND=w)l6{4)yUCYlxX0FiD+v=LgIRb}VbK5+8IC{{aiMH8JncK;Y+HC@{@`72(| zES`;9bO?ucN$C&vHv+)hz#m(fqrlILE8B(EYt`7ytwVbdRVE=7twc+8Bfu~|IO?Ya zjS*$XU0dR*gheATN;YWKvM3z-JeZ`K7gVl+D(S>;sf^TPuNs2l_!(*&!A!IZ0cB&p zPT(eg|3{M|$I=$vm1Y(T4bFZe8W+p{^#ce)*S}wi8xGPG;%(s#%Op$#)v0lfG-)HZ^lw7{Xrd^{3@IJ}`waHa7Th1)G za6083qZrVB^gzWWH~^mO4X$Nf!UH(k5*c6RMlFlo$S4yg+9xPA*8d7|xX_$1uILg9O?}iISzHbo18zGx z^xa@k0dr`uDD4pQgF13fV+@l|iztYQqg$$7#me`VfA4 zX%akM{c6+x)XZmPf4r==rq>RcG&H7;17v-;P0H4vH@-+jLV05%@gF~&ZrtjyAdNn9 zNLu7-)HIdZIeqgl#t>`}M>Sj|VU!6N%EpyhAqw(Fff1+| zJLqV;*yHA7Eq}AeRp)h3c@n6~tj}&T+oWuWtbCVZ33I_l`|0BR^R`wZTxZRf!P3%H zTjwXm3K1w!()H!Lj~LkjdBed4Bz>WWzbbA@)r-9|eD~9V*38}{OSbsN%fTUVj8Z1n zsF|&YA)-Eux!OwYGsk+ybPfu z>VQ-pP7vaC$6L(q=xY0@itq%~CCrsII_P!$8(g$_yb$?Dt2Y0+_4wy8sh23vfC|GKtOSRvMdiJN(Eg64n^{V1T$mh%!-3$!*c0aVG1-TK3Mf4LkvRTHEKo~Tk`YG#rWgzIfr{{LJWS-%ESWl{{8`Na=n-pQ zCOMj^SHf|Fs!zV~P;OpJ9!G3!-$8$MHkZ zzDNHk0>Y+pV43{xM^*Z%e$3|eNDQ>yj?&22DX>S0gzuc=n4JZ<>NE;b z*|E5Qo(%qwL?cI}iVh+s%K84jrLs!S)|*$W=AWQV9M)3{Z~0hsxq+87@25NMl7t$V z5wh0w;O0eg|J%!;*Y=p60=?IS@$Qp-fN&u90ekjty3$DX z=?oj!aV;iSW!{lK8f&ITFZHnd(&uuZ*+Iok#KbP#+ViJHW6T?z1NC}MJSBnSpwbq* zdpt)0z~(0<1;{!q*?B*Rt685MaZ@xyWyZt5O98^&U zfcp0O<<={Z*05+SI$oS~Ql1wZ>1py$eN>s0^B{PDHM7dNfmwka*_DzaTRd84`+Y>0 zbs;rEVnXW;&=?qJ+*`GTwZOP$I9w&ByO_EEh*?Qs zcYXjzY%X2Wd890q&r|ZkP5=0VZ~WL~kEUjAV{!Y29@lg}MWWNVLTFznV)l49s}iy; zbeE~gUf{4T%*;~)k6FYmQIxDHw+eA|eWKJq`Yrgxl59eF-1!V>UMc1iRkv^G>+k`G z$nYjDer^#3YF}NKnYTQSm(%3bM#M)5l|5`K2XCmBe zDffb)GyNi`IZ;m+5L&}AQY95CQmRYCSy`LNDr9~Yb<)KX$du{aot65IherJEdoe5B~|;+LqZuc_g~K{s{G9C+YB&a`8L1R^uSP^I7#W#8dux()0Ru07NB`J&gM zE=n2UH7xvvlSv|hOsVNA7f0`M6(7!EoPs!E*1Pf~l$&+^5Db+Q#(huYLKlzS`%hIv zmd$s8l5`%pb*io;#-*|7E06gTjAn~Hk`@Ugn7lt1aXE0nrdhSe6gLPK_$%AHP6rg_ zQyjiW>=Eo;)AfEB2r!^TBS_`5GFLMN%lOrjT^dgILv!?gmh}H2?W=?0YQ8oHm%-hG z2N~SmUBaM)JHg!v&fp$g65M4VxD(tG+(IC@1-D>%ci!Lk)o$%STf4P2HCV-ykG;GKL}JPtFf7wc?4ksYz;~z3hU-&7rJIQfw1% zzvQ^a9EeF2G}ubnCBUwvV31{JcCv4ek{A1i9`W4Qw;aVZA&s8mUso9OF%3?*PO77V z&y34WX*t-HYC_KBcX0y@r$30D28z&P4^jVxdrHBaBs}cAXcA{oJH&7Nma_}0)-Bs@kKH}!mKYVlja1;m8XBB7>c=YnDMNBQ;S;g|OBJTtbNXk~lM(hGKSX z)v}ns!rYJ<*{Qqo&$M~BZ)C`uoEK>$aG1WfS&maU{e>rr(9msN={mjf&i{G+(Q+OZ zA@+m8t^blh2`>v6-*>! z(xFICvR{gRuweg7|K@Ro{FR<}O`7`BW&a%)9-KkM{n!l}41X`D z_0RXFPrNmW^=|z0;oYk!oS%C*bfI<(3KD08!;>Q2o)YxqiQ>A(2jIbRQy3Ejzrt~X z9amMYHeiu1nL6L#`{qh!zY*s~C-w>V{ftw4?q8C(IN^0==^Ieavew2Rb4|MvK0!PB zhw22UG*XU7vxlejwsZCNnVp`Rd0acid*;KO`Eu-%>>jxXgYs(W%!R0^CN=wa4Ei5Q z#kgdDeW*QS{0C6y^j~E^k!xUI z2d?*j+5V#}G`-I@1|88Tz51zZ~t8 z4m&oquITPgw@djkITz(rxw5S*V?IlvH^;;`JwPkXE#vM?eK6VDh0OtbJOS>mWyg-~ zuW9d`9#rV2);-C;SHhpky?^T1rE8>{|Jg&C%+%ZUX zZ^Ux>y-=amH$TKY#wRbJy0~<))E%rhg)pG#J3(ir7 z@k%c<=xGg}GJ^hTpf1`mA6<(1Aph|_S8c7GJ~7jt10Ut-WYZ_PL&$y9`W&Z~=w=hu zZ%f!8%rUWkTq1y0c5~HDMTFw8yb-;##<>!=69UCMdJprEh$Ti=W@b%H9{`fe1;j+H zX{XKxRZ{$fWLQO%PpFTQ7gi3*88!WSKm|2qPFsd-5R2Cd)qxHi$oIYb!&X#>t@9iR zq&31sfu0o?pJ8%Wb&LfLl(Fa^#^lN`z*c&dA{9s?f@r4okndMB*4+D3d;sgWqj>@W z1w&-iLl(G3L!%4IMPGr&g(`q_wE{)W^F3q7QH218Hf;o_v_OdbJU-|>&(*2SaI)8y z+?3Ji6jigS;%7XmW!Rta_;f`O5~8Qs^ViUMm)PHnOl0IwW#{j7?wA2$(GSa|$v;=e*$)-s zKi!{T03X503SEU9sy|<*nm9w*Vif@Cj3gvM+OK?)9yWKTat z3{BSQ{T93^6t96?7c@U_3XrqcIb}Hd9;@nMhj*BavHZ~;so!#1HCJ^YleF$LpidMR zW8JF$vrMZQra0ZH=yX()Fy7U??}l$UYPH-R5Cbi&jOde5CmmnkYx`+eyXBvUMQtAHa+9X`Q zZcX;Cw4S4S*zbnoC5v3))>nT!oAx@Jy#G?HK4~rD8r+zK2 zkIPxwVkhV{x-YatLhR{m+WIeGyM9pvJs}PK-_9(Xya+2S)>(h`7>d5A^wLAtrMx4n zDtHjd+l6{T(>U%N#bO3XtlOLS6}~C@4|U4@g(zBx;TDqpL@$XITL84%JL>f9uG^JB z`<zfHY0BjZhg_--%M;1TxHz~C-FG#1HPRs;~@SN24$q3r!l=Dj2^@=A7 zX^e|}Y@AoAo&)%K@gKl*U~+bwrDNJ^)rl+$(zt%WQWV2ax$>8H^>7O(cMZq3LrLG( zUc{>qiYRbvaCDoP2Ll%hQjo{>cL(yezY~sZOq2Irw%wM)&S6ckj5BRUG6zPR-s|hD zs&#d-ma~$EwU_F`U3T7r3c}tK>jD2t^Ddu-dJm?=Qm5j&ze#V=?Y}=)G;jaV z{(qK<)?cjMhO{F%#~)Gpu8y^HckOMGSLAxRvryxjv!u8Db%lmRhnT`r&jRHT3r6ZR zcxT9CV{cBs!R8U}N0r>J zxjU%t*2@XE*U9nKqO314huVy<>fI*G46S?+l2YEmGhbo?a)7OUXBaxx3A>YWb4n6Q_bncas6b;WG%(ARoR?>k{xBqy zPYtNRD0{%y+cldEp`qQ}ebcKoY;eY9y{iq4D=4)kd7has=aZdNp()4JY{kD%Q`G!C zEjoY2J+B1J2gMW$s`GUurtUenz-nj^j8ovQ7&h5-zwz$MPfH^7Dted){Tn3J{kPVe zng+*)9^8^t_&yvD)wfpd z*lrfVM8yjv{XppS=QCva%_VLPXc?eGFycNYjkZPofoxI_RF9LRN*w zCl)e>Ln@0Xb|7?9_eGJx9liBJmSsD!ntJ8C3#Suf+(;q|#+hgMA%tWb(V{D@z$N&U zlsejhEb~n!+203TuF)kD0%dJ(jy&~WXWKdm=-E6><_VTp#83~4W=3(r3zvE;ynP0) zW=Wx51tC(j} zfTpjR5W*^PEu&2~#N50bZwZM4++3MM434!GXzKL8-bG-dxxZ`adx zK1{T$LN8CqOD&u=Iv3RF(Zdo)O9jp~EHbxsA?VXyk-==X{-d({kq3V>hDU7@IMV0$ z+Y8YvOv-o;gW3F5lNr?EufMQa*4SzZY^YpRI+LCvdD-`1gk#8fQltuB zw(U$KR7R3qu0{Rz391=pwR3DMTO0K0aC|;~{MZU=c0d=&6#esAw|n#JC->eTsinCB zjC@c6TC`42fjX&QG)=JOCUIMHTQZBddiejSnlg_S8CzC$zAv?MQ{lH7;@!wSfhUA9 z{pa(VPhAya)hh1V^Vi+6=dqZ4vjN4Das~~Kr};6mW)>JVG~ZH<@v~(Y>*0>mJKa^2 zB&9-2%!vKByUjP()s>mN?*S~Aq?<4}DP7>7(!3eYVb_wSvC(ED0oYV%Tmzi-cQ55Z z>f-KUg5lJOwzJ6hcSZ zLu1u^?{!0$)}1-ibM)vP$bo2{toL_9uswBy$i2AiV=C|^$Ql|x{p@>~k&4ki*2Wlg zkg*o5lH518S#H?`xaV*DP`eOl$SZ1|wzW9(w%eU`n{+yi@R<;0W0{7#OgUNY4Q3K9 z!HZ5P+a&xH9GaxICu!Y$sEUyN%(7zMzi{%ON$ni=cTm9KWqZP?A_Ja>zhg0R63S%rAq~z|-4ey6ml_peI?GCLHJWPCA`ztWW!6;Oycm6Ob zek2#2IqgPBqW#boWAr4Bfn&|Gdd{T{Kj?TM?Nc0;97J`P$mLEF;I%og>(e+nPIHbr z9sB{Oas|Sa8NG4tMZD*mQb+jX2r>bik1|zn(D+)Cu@`$UDsES+?Lw8^g8Fw`Yh;F| z8Nik!6WOXshqP_x{!UkQ;se6^89|EK`ZRh;1w94Id(LfMmhEh9Z2II{#0Y006-^{w zv^zm#lYLn>4hXU!uK?Y&$!qsvbZ4?87ly|(5}si@LSUaDq|86qS@1JFTW?l z@Q5pETmiLTErW+NDfi)|!6;05hV~)6bJ6I@tTNmtjdqtUt9LR|TOm3vo*xU2fJ$M| znzqR-^UMYv7HS~cr3)*0Uhl(B10ya*=&)zitot4sOQ%}0cX-?0ujka_$xz&E!rWpq zr&I?7HXor~+TX&jbES95eMD~?%X0Wku*IH_AsF;|CiC35zI|C$`x#Bz2Mdht{ozC^ zZ)MTj3*9@?-IoE6Ual_Rk_FY}BT1_t&KlM9SthUji13i`5Sr?6XjSO42@=Y5dv1JlVt=W>!eZ;XAqk85WS))==n|%DY z=(6Of&g~LTh*;BeapT_t67<=j%;5&sP31!>j0Jpe2n`tc%t|7$&iL)LD=y&jvK%MM z1-2<4a&Pt*PTK0iM_uX-3nNk}lJWWNu30R%gPnL`?2F(g1{}e*-0x3XNYavJ|1O}$ z^jHf*U7wdbaw2HV^eJ$;R)xKQ^%v^#z4J1ni&4P$vw!s{6w`WGgBZ*nZiwM=)+^+~ zpCG5ii8HQHOdg@NLN+?L<|dhx-Mn+Lb(a>Zzy zVhglp$EWptU(0x?-ON&}dKR9qN@F6I+EU z6qUX*b`{*L$QPa(IJ_Il(M$LW?dXP%7Wyx4A!U$^aN4HMeOWZ)Q4pYNE_I#9vR<*_ z(=S+4TLx_J-q981G?E+%-0Q($tm0(0w~!vw+wZ^ZT&Xb8xxR$%^EOJxTy&R>hG}9; zZ7%tppXv_u!glJiK_q3}TnPG7qb-!ZSykHZ%jF9{3C*6fuOadyHDnI3c$(e!i%wv! zg0pnHsqe}S2^D4`>rtSXXu%4t=+Tws49&9r^>wWwIF(%{5!4x{^7B%iJbt?De(e0$ z?&a-j5hbWmeW=y&d(Dz?%3$!nYfB>zJO3d(7HE!X=N0fqUuxJtnfhi=ussuJ)l1R= zKhYvmd5>mo0@F%vI~NDSq;O*U;P|8zby`Srtvg_9mNmI#C) z)LSEzMWSr7DroU;UqK%Yy)0rcL3_J2Y>Lh}`3?%0?HDFyp})GRj%FhSD(0RAZRq2Q zV`KJ2j7gkxe9Ik1-kx2Z$O-wE4+m9?Bjj@Cl9+*Nd0eASOc&qs@{E-SjWh`2zid%X zMoBE56iiDYgxO8C+YRx~LzDL?&3cR{xTKt$fCVM|)wTPmx8N8p0;{0o>hg-zV6(Jg z55iXRU)+K4imMF6+}*%EtM8LZY0;+He1!uTlNg=)Tn+YBcdfXKlM}mqi@~VzT9G>~^U4<=J!QVWX3eF{Vcy`d$-?dn<_c{u^>tV>eVTc!qGp%whJU=c?|Edhy>0rv2 zeDpI#k~Y1KQ(`$>4zNmfknz1lioXwq7S@1}sskzJfdC_6fzKWsNwcZIp3fsl9Kk(0 zz(<)lWR5w!A2nGN5p)l0#jup!3zIzq^TWEo@PIc-Mf1kFr)|J+Oup_P9TWN+lI&|=YBCQ>*6n6TeJZS_1O=|5MYdu^2N=e~-Gp`@<=Ke_eBbdzt zUK)~F6pdqe*51gS93bAqqpC3Qa+2jg0KnO=?R-|WzE^R!Ndd7>@4w#*CgTA&@Pk>b zc9PVTi*3}BIJ70{zZ#l+L4Z|5PDqbSl(OZh6u#O;8R(Vt=d`m zAvul?Giav^+6vJSWA_d~RjPJ{{YhC#(;qr22!K?-LrBX$ZB8rdex`8Po*Gv1Dw6Vd zfW(ZjBwt<5;)*TfCJ}%zntH%2g-5PsyHoH z0{Be1*aLR{rhL8J6q{BZA1u=^`jr!G(-!PY1iPdYbh^Un(qVOOquqqbYU1dvtmR*C zW|7^d3QTT!1lQufEgd7`z}K-`hc&4;#V$rbyolqXhhq2Z#Ai5(Gu+eZJVlpr!w^0e zA3}1OlaP~(^^=dHy4~0JOFPGq- z>EEDjg7Zt%zrv%-KWgBH7x7{Z-XL=YVY8?)$|aPa=t8Yorkw+w^wFC~@la51QDJ>E zd_#1CeOYx7%eqe&`{2^&0e~ZNI=8UrmPcF5^%#*SL-Knq$G5+RXRN4E!+_T%R`GZj zZ~eW^*D4^h%4-_g^*lV|RkID{(&UW|i*hUs#dR;pGa;EaT7ct|Q2Xs@sa^{wK@vj- zH0KnLzZvS)SnzD;(t!M9{S0v>*QZszU5iZyqf~|atc7pcaX+o6g7U<9o9b_yH`cR6 zWLs1@Dtk>J>qnug+uLxcJnwZo)HV?k>azwNv5O{~YwP&U_KR2)U!MZNvEIxZaoTiz zC$1OG!*t=%zicFcDK6ES%@6K@MpRn{nPmwp>YBz_s*v&)4mMimy}6u;DQqjT%u#bm z8)>{M$0_&Zh?UTMZG>`aA4(RAoT?Go0PO)30eTk(i5sCLN+NyU$hawWjQjL|OWRcL zC3oOJVlp*?!SL_fLG!~}pIveu!HLCKIF)IOa8HhHnjFxp^bM^8{yl>La-yNGy_6lC zPZxxT06;`UKte=8hVxSYOPGuRz^CJp0b6?Dh0~@LH*Iqd&Rt4tSzNjQH)Ae>6x|n+1|xEQ@(A_WmnQu&qF%Ch@fsi@%N29tsXqL=vJ&yC%+h;rY>W4jkc*sm+ibyi zSZR*F#*xl_pW^Wp!8AuU(R%JO6{gH8jWANKIJ-j!f5DfcwRJ>Y+L8jEDnBGQcrb}Q zXi|r{Zb+zz-BtZ{+v)*@@_lJeCJITm=1Sy4#;cVxQTsG1Q1;*l?tvsFn?hvXaWa!l z8NO`GA^5YaOj<)9ZN^WeW3x!Iv=P|j;4-*~3SWlWvspi>(w$<-cRqb-=w#2Ik;#ry z6X4^Bv|p_CCAl`&Cdvr|#o@Tx{*!R)Ko5BYriKW|L3P&+nVvlg2kgm~Hi114sT6H5 zn+@@9x+$J1OKFV{F1f&||FUSWfEsS7;mRU{sn@D~X%o+~@iJoIA4@6wwK_7wks>fT z1ut>CwZulLf%)@wk9_o==UZ+k02>ERCv-n8t!XyL2*b9wp?u2y#IM?>!OHPGH-m=} zaQ11~{S9H+Df0P_QEB@(bSTQLiMlmGZdyk%?6A$sqRwXeFV*Z}Q>>$o_GQ}yn2HL? zQdjNET!;!LEhaWLIkcJmUMkl7B@a+WiY7fWv(6*rxy8E#0Cg=~9LFuz`jkC?%>#3w zI9ok*CbI%$#8~c;Nvf zL}oQJb=XveMF18|q!jg^nY7-xActcFgVQREK@9{`qnIW{&~-DKMWkhAW+HtSHoZ<` z!J`MO{r7qDR`>PHju_yByZZ{yhd2P9Gg&JCQTr{lg;)CE=$5J+s?T(MKnKiJU9pZ; zn(JuNG7Hh_F4w20Md;A$=@+RyRESJC*zo|DY|RT*hT~AI&ynMv1 zhld6`J>h)z{}w|lZmO@UnOBR4w_~@i=0_%-p8He(01QHFnEQD7QHa$sd4J=9;%2tH-t65y(dOg#Pno3%jN?o3UI~TqA-1GsbU|t1%~wO76CMh zBGUKY30`%IeB+VoWN;k~Ea+;U&>=6}Nynq$r7K8POIDplTP;wWt_c+dyQ{KbLu@Ed@uu=$#Vml$B)|f4kudbKp<{k-V=Z80?bo%3>~Ql%K`N z{y{#QIkyHHs>3n6SCjdwKxOi7*}k>b6~Y~_=1=vZWZP&StpB^Qj`%l_BA6j^=Qon+ z2c@=h_lB-ymwC?7gzDi&14o_y>17QG0;Y6~PcE9Uy_ee*^JQLtr`zN?^9*A$DQ%Id8-!8Imr*6*@@ z2Om*6^vNUCkkr{!M70lP7)-O9#sX%x!pQRNouzo!g-8XpQY>kV#jV#R?A==H_h$R) zr_{8=2btrKRef!-`Adbx6+RzbwK zOs1n3O6>FKImp^=x2Rhg#Tj$m#>1qUta>3hwhLI0PbeQb!|eT1aqA?se~akN%X31> z_vadN;~>(3Euu=Sj#poP^f-|Sv9S7iTndm1+-Fv-`BJ}w6TR|H$a9RoA^>V>e1`m7K7Ct#ysE9 z_fa6GU*eVdrYYFYVzqImfIFqi+U$HmCd)!&0EPLOPkaO*-0g(tu&>&n79hk@E*!yR z(MXt}MOKkKCgJi`3^LFB2UR zvFk}P<}sB&NQU;Tex#(6zUXu;HdhOUqkI5CHpypL;}a2WT-ediNqOOXIX39H0-z{G z9%uN=K1pebzB4x{lE86fSvu^#8H#RP=RWecDfwJHLzx%yr@ zb(5`{1;?BbxghXbC{1(9a}ZCQb$TiAzR+Lo+}5m!<-L%egnDt(R}K^xEnA z;%zBoO=B(6>LSxDrZ|^Fu`Rg1zr+8k1FTrn(c;l5>iV{H=rM{6Gx@m!!tmLd6v(GT zjcrRaJKh+*wwIqXkZ*BZ7SiXy4bb17J#s&62_&ZPbgw)U&iGK_S~E)5G8E`N7K@C- zud^g>#{@_&WWs!!Qm5j3Ae+)i4Z8#H2yliQA0-p0azF-eHk{K3e`$yO0}w`lWUgwh zekj?sBUPo3+|r800`OD4E0WyPzZsi}de>|T_X$KSH2 zJ4>#MbZD)XX7No zrB$}JBF9M7Ib+4=Gt^z>{fRe?rVHH1&tqv7Qw3n33$*d%g=d`Dn)|xO2@(uPRoV9> z{XIqX(ralmC!j@&0Fa8=f^rsYDVUDfJ(QPWC$)MaEru$D{O^R5q~Kpw2C`ix ztBVb;E$E(g=VEOhk5cw$8X;fps>8cPB|W1UwW2*Db8PcegVXzzfNiJ^y~ME4pJ}&}voWm-tnq9)Bp0xxcsk zhv^p?XS>rG9&9qspW-HieVKE*u6_AYa&%oDu=rMNut_rerv5tJRpTaMI3KTPbL8@w zxkFPeP6ChvdB0U$e@B)cN9RpkKa|U(i3mv0Lkhg^pIs!bbUN3L_U^>+Rf{l9>`tWP z*UGd462^q8F`c7y8_x_Y=sGV+sK)yvv5*EuH*w74W{{8<%?7E@V#r>Hy_ZIqtzk9` zSy0a%<^~T78}o0|Ot235^Y`Xj(U&ZWq2VFe*KQT3q2r4=BuKG3A^gg4n9=@m>P#F~ zj1Ow08pYiF%qaWL8%w4_(_pEdt>|LCK68jEXoKydVzFu`dkO80TpUx_ZHzu(pst!8(Eo+ZQF6* z1cIAvSId6uZ`@Q_f{}_#T&Yx7tAiFvtPHK+_&e#ba@~S*hw_WiTKb*~voog!nF(cm zD!q_>6$GvVZ*AvtYKvG)L|a3#Q4@Fd+MkRkVn|yH#~L4V!qFF zYH0!a#8AW9Hh!rvp_-sn$NxLBRrfT#t)|yt)1lF7ls=#k{ZUT&PJ8QZ&Klbfd-GUG znAViIdDXU?)~_`!fGA%4Xf6J^416C|e73N}0n*a*v$J(nK>TqNr2_>bti`Gps)*z3 z`DAK$8Q`fJ)DW9&RE5pAG0y4N z;-ZvlA4+vdWrL;!`*yWrky#Zl^ft3FM=$S018QE#y8D;fN$`mUtiKv9jffsACud^5K z@Ufw|$}2u3^|(b=!2F)EH5k!UHK~G-HcH#OjfF%nb(J8%Fu?*?lu@K95G>CbmgI| zj_!J1yMMW*5?-KyQ>)H)y+{eVm6|U)2TR0I{M9~6O9L*kpl>>L4^&-Pw2wzr?@R8Qz)mkJaSyhxo5pHBl58GCk@dY zHqY8++ZZwu%q9pOb+#;7WCLNT>Yq-_jG9bPIbEC_0`5-?F{TJ0_ijRD*$Tyw{*VX> z>FN|kSglF7sh|QT*izwM6}KKR_|?x9Rfo1l)h9u|0V4t@W)i|1r$%h>zDIwMg;>{2 zQoSK5o>;%dFXh0>q>+K;kt%_oe)58zy-I@!G*_FgpM#~DrU#J;@c2kb&^4f)A{U%A z7%N+Zy>NHyRi9r>)Ydf7?;zhT2RJ4Y52u`bW4|p^0yO%vOj}f3Oc{d~hPY+AJKS!4 zfYo`(-82uWU7?6iFQ-5_Hnwt(n7kQMv>t8KF68|>En;LWWp6%{YLv`1Y^^Da7JOkc z(zofFVDbT34cB$X3JF$45dQrNe?_JUEziZyl+GR7)GaoWW!?RWLQobqKfK7Dpnbg| zN{YT8gyW}!&!^>eV0~T$v@|UkR*XjZr1NNbbQFXvM{NtOfuQIFY)Dv>G5GX)VS;i2 zdqtvCMoeT}g(J$GSVp>MTrd>J!IGzTy!leyt3eUV)SB(-TFniyA*03h9SC9f@;`t@ zfSYV@snn6>QF@xCFb8fcx~*~h=XC&mUaq>;4xvimp?FE}mjR=@D2yl_y)r}><=>?D zF+?}T1Ifp(k)vkWr`&S07x~B>Jt@2ij19f-w(zBOS1K~KQjxvM9By(vtVoR_M&liL zC5wDys*Kt2j()shwf%O)ByjySoKz+pfOHIsHEl7i-3xRWxzA!$oIrPO4ld#>%Q`21 zi2X7iF!wz8r^Sv6IiTvRyBU7AY56Bx?WL^Vg@E|V#AswrWR%Du$*Kg+DM!*U|JdOq z_Swy1)oZ6?LElZELXF#;tzily(<#X2608}QL8esXX(pBa8V9`IIZcf-tUPHSrRg>I z+sZkYmBiozD4$9nn2;}i*QM=9fR!3{9KTJ4aSMZ+-n*4xJ?`l)lD zg{ckuWq=zPC8uw$>q=#?#2ihDJ!^h}cr{QHpoT^URCR=G!8( zD|~@xEQ^mR#=te}-pP<6cAUmpW47d<1}#=nN2CsT?am(nkhg0(zM7RN8B1(WnoEJTEh$#1}_{OO#C5OKNWhJSQaLDvzN% zBf~M(^_7@2+eedGBSSGbdY_J(iT2vY8*}X~9sU z^aC5yE>|Hzd&UXBF-;Jul59F+p0_|-&7WM}8)|pb+gqHUz6m8oPXOAo4LG+xkxko` zSXwX^$}gZi(YzqhN38(n3WaFs$Li3YF3X!CJ!c-NZdJ&SusI}UUGNux7YExu(Pov? z4FR0!aTZ~@om{!u@wh6C07KWJ-3v3poo2eX1;*?$tTePNd(sD1&@*ZHe#}l#JyyahQz$L} z4SNB+eRo98j>unM$<@A7oE;l>WCYTumtGv)JFW0HoGs6UUx^>E!{f-gxu}S-52zzRQuDnDs4?=czx9x2>Q*Znv?7 z<1=_z$0>C^D>Y~e_~J{wOj}2v|9hZ5wtvSnYyGc5|E-&EH&)nHM^k`1iWf2CVef~X zYX!c%Yj(mvk(*UCV+wK7k76OzEIlEzWpBYlU)>AFOeHN0CFKHO9+y7ka+_h#8a`yZ z*cUbmAXYKui%4LDG5I5X4i`-9A_u$()gm|>1&$nx1nYH~R|Tx3>@&tqfm!G_troBQ z2;*vG_s{xK0M0SxHNI$m8ISOYXbR+4$i@v@5zUR%9gIV9_18_6d4*+yw2M)dzy4fi zzdbE+jAYmkG`*@|m=pR4G7&ZI`M$r%!_FCxS^ z!5|E=b@hfDb8;~Ci^PG(S1cAAA>kh1Vmujs_?ir#dARIR$pOX-hvK3|h%4CB|^P zS&jUh;m#WDb)9mP(}OxgT;mG`|=Nwd$1Gch$vk?7-f)H(}9>D_}3)ZLmYpfh<7W1o=AxO9S<3tjs#h z@TYqt{%-9Tw!><*k}Vhc5U}=nSbxUX9R=-{Q{oc{F_pfnpbW^A^uai9d6LD4&C#7k z7q6pb&T$jNYaCNxL$qf|&rtOO(bt+R$5LU)WP}VuE5lrZNubmO7T^<=(5+5g*GGrg zQNGO21fh+9NEv&eQQv8QU5QOMA{trf3CPsZkZu|YnGMiSdL!PL4KnC4vMK~SlSV9*l($jSibwBI23WO>S^+A zbCe`Jf7L2Qs2SuZL%nAg%;8QGWJ1823%g!6@2}v$&FfSN8WLK<6|oMi!mWC(a2WKs z4VlFV9~srgW2#eo!|3>u%8eor*)vo{un=y<1h8=)K{+sohU2j9+W?BFWEgDG+En5% zfd_cTH5p-o3<_Jqku}^X*yPl_Gqq^Pn1ghSWpjo-)5SH*}FDSLmZQh`wcLNO1Rh zmOX+w;~*gX#^<_#41dNmqQZvC=5>GF9-zO`>nGgZI;Q;b5#M+%K@}*xw=0ry#`@4$&q%73UdMp-jrqxRx1oi=u{~R4syE$dVkVG?xsIg$NzX7C zcVCp0W_o<{4nOzKC%Qn@9zDr)qswqyP`(6-h;})z!~pWaKF?*d%{QFYfBQ+-k(jVN z$m>V09_~Z?6PJy8bip*?TVuSZkKig;U4z5TpL6q(kc`{g{^JmN^6jhqSUwfksC~$AD(Y0XoCLq@% zB7x=(8_j~hm>;sp(^B40w&PqHA=|Y^z8zhhS=7ut~KUMV(@r`omtGVXF#cB^Q^pZS9 ztqjTS0(x)wZ+bVAlHF^q0= zc#HwuuW4^NUgyYB$m3l~{p8DWoBhG9thpTo9nsykP^7wH_f|Gs(4tzaaso6(OlyD> ze{|$trWMb<6U}eLaH%rwvvB4->seXX{-dILdKdFbSapJ^%Rj5vxcoL4^plC#ZqatV zu}Lbe$W@|%t7ib%e1+!q51{#=rnechzQMw3)R?(mi88_xVFGFB^E!3;AMn?@tlaeA zh|^8Vzi;jPo4#6mL7uhb*{rLkiE7PzzKO(k8tzDN3+dM-SOzDPIrgQ(r@N@&GeA!V z7j58ZxlQ;rO;qC@-rSmfR;^jZpq_h50_YZk#$|qbNEi1$sBiB3kr+20QM}9e#RoK% zV6dN6j(RIO8%h8yaji3!RHlfgVJr60=!P}tv5{pD!t)nuF_0Yb#)I(N*-EN>F|@{B z8RA09T48!S5TGn&qvO&;r>dni_?yq=VzoJ**GcHyVzpVu2jc(UF2EnCvi@n?zt&tg zkZ z_{I+2r%Zo;ll$pEJ$Ck1yAClsEUA%ybYj zW014`9XAq9>bqrgyICFmT!~)S|AZy8@yiic%KU1Gl?A#?r$%0snY1Df8hM~!Cd>MQ zIpIxSN!lq{6Se2hyH3n`w|8`r4{R8URoxek;epN{TYpGTOT~QkFKm1j!oY|p=V$~` zj*-*utgTUA?-{V)>xz}CSAS@ux{xDSmR+B*^gOGGXsI*TcdV7hc9yVpNz*Lr%JPcy z|3`$ve-FRy3mUr{Gug3F*vVFi?X?>(Sb{v;kRUq5KE)V$fmVkrUy^z&SVd3Ch* zNQ2DUjRT)x~sia!;wA2jri=Rfk^eLmt z>UXYq?!rj?M50x~Xh~?^E`-dLlFb#(&00z7HL&_v>TS}i;uloQnGc)pNaxsyTJ4km z2&#@}qxdr0Ua%`adRxR(sM}RMC!zJl($9EuL0R+#F`7hqUHAaiiDCry!n;bWsPBqB$9L@KWP-EsF{er{z z&KEtKDB{N{H-I2I5>s)Xy!^kx%C1&BMWDWBvMKZHFEipgEk?KqT`Lz^xX~F$ zR;>p1zFaUJSNc9;!gqVdsG`i7UNn1CP8g!6cxdz6GQ`?Jis1niG}qLo0uqj7(+-8T zmD#b<6orxlMwBL*Y6~_NHId+A&hn`{2R#L&E_%jBJ0%JlJGYla=#s#Xg_GjfF?k!? z@H_)lNzXXYT0`yUwe!z&-tT@t1*9^#rnfb@5vLcDF_ht;dD8dGRQ?feVU0UtIK}io{Bnw$ zpx2NYjtuG|V!M?0D!HD)1H^{FS+dg=D` z_*L#T5D|fj0rL+hq8MSB){$YPIGQFCaRZqeOJB~@;8vKpL2aYFEWUMsUAH1}eYyn2 zu$DB~XKDw5Fe`JfC}vtYku`c~smwQPvbG~qPnFHK0G2(4^_$j{1H^P4i)xXS(bcc zn4zOSLl~$yFSJYiD!m}nHF|KA!16xIt7AI@^^1!DM%)?d7M zzKI^B9+xHqiC$;ZPm4In9(;?y6Z4x~zAE|1LV5D2?D^b%;G>RWQGh2E#f0za9?py6 zH3+{WCR37^kgcBeh!yp9y}FxPdXdYT-yT6>H8acT(;jC`S!m`xRrnaMsKVZpHJssn z>uA9%X|}*zYRZw_bT3`DZB+&o_FdYPtHgJMitB5P*D_-qS1R9z@TgXtF3dRIgQzCCJ&?3o0wfa*$@2$q@gq_{ zWx^@ zeKUCBfd=(=LJ;$e6YW>2=x^cn$=uhwscp5z*6I(b>&B=7-2t_)0;U7Xzy72TqzxgB z`-7$Nn`NIwK9#HL^a`Z5zjrFpw#cyLhUE~uit3G0qPCn>+P(BTHetgo?R-zLh1FZ^ zsFQl%GCFR>~WU@cQ=L*`od@&D~634?W@d`y;r3 zK0+k8jHL7a#cMFCqveDrf}GEm3{q0)?fsg$AtQops}A9E%;Glu;jEECF%H0v;RV@ue6R>ey0q*q|g$&X=VV1No!b8;VwdtSy; z&%oMi7pP(d;%6dHH3|w_`JkjXsAaOCzm1Dk+p>IQE9sU`+rfpJt4j~#T;&8^V^(4i} zBNs5N;}DEgpwQG^WKm2Xm`PD;^!}0=%JNG7mo0fVJdVP{}dxk03}Vrl1j zlsHZx3M(hM7@+j+RkAU@?Ku(l<-!%E+UlONX1l>#dA7eB*MT$0z%aXFL$KYv{2Vv? z;hX{&#tpX*W-5b_bKO-n+FT4q9KA(=Dcrg?-f5BKHufo4mcQNiVf0V`m_QTnogp-@ zu6CA84wxj-FZGfw9DRWZfg9LX6yRENv3lWq_*&!*=hVeQO8eg1{*oe92VUTMb!_?1 z3Ss>tF0>nU&|OB}!TwF5$U6Iy|-bsr0;}{CD~9zMl_{(4FBI+8~`k`HPh9 zAt(%YVK5=i?WVL3?TTf+fyKunUy6_Y0>_xBjF|bzeSf;V0$C`!ejmF#YA||c`Y3sy_huXL59W@O$k{=UHS=b_f zj-Hy^>Iy+fmyXkmzj<%vl`lWwIGjEKp(RWvyy=0Xvc%h?c9&g6u|1?)%O%?3pp!0lwOR_OA_!s8|HjRvyM>TQx zbWotn&jghlq^W?i<7O}=)-185$JDTB*m_6v45vL~Y}@Ou_ND3LwJ9Kf`b@2K%u7oi z>yfyz(vB3vEdJN8cxxD0t*loCva@#K<1*~GOU7CsVwaE8=~987aK8l+UAbMk=dZue z;luMjxg^a86sTcSea0g^FYpufk|M%_6a1) z!pL(u+RBwgrWnpiHl*hBEk?`fQ&Esh41mmMwAt+U!T{H6wZSx3t70lA*~U)Fb`eeh zW?w@F+3TzpHs*4!iM+aw6wGBH|GGNR!C z*4z&0gK1_dAE1AK&?xgT_iDy6c?ScQZv6y~rLdpAJpDrKVd88liZlpM2TUKjCmZt2= z^ca5@&PZ5E$?(DB*L(Xt6j=d4I5`Kpw# zPq!rI{y@B_8S{70r7rSpg~2|y!zkxwSKzFwntGEGCf7hGs4YfD_GmO94DPnZ0DHe< ztiwYTiA!>Uln@cqpb04{z-tbx_t6tOxA%NY424CMquFWqmdZdcoFblG*KZmSVWa z((Thb+JwL{=b8d*{qtqx(+V;EL3RD#yfp@;dLCglX>H)j^Qt?~61lGWe+bhT{x*i`M9P zLNMHp)Pg5&*tyI6ARH|#KbmqU^YIBeM8MBd`I-Y&#U zT!$a}vUyi4i38X&?R!zJ&(`kyqsQKoRF`{W z%l8&cQqMF@mJvOoL6<=J3eq5*Mj%Rm5ovispZ-27-En%=?Gy)qxjCtyi|hqZAUsqU zcCZ)?xF+~f#0v9EJ9hhw;hJA&a<42kIRz=r)8cG-lurK%fT4PY%s4X4*`$B;JMh&@$ z)*QM+qIONHG1z70G&YFNGY%94s7b z?LhP}<99b_QW`^^{&bn3xyIw!2?;kR*WY$zUuS8-E@2!}2(t)p;L$9djd&HQ#o@Y2IIB|Du>G*|AKZ|E;8$2LDHnn4eWt#`2!yWW6 zJQfE`WpM!6Z^Sn`L2(Q`MtX*7)s|b`;qvPO!;&4L%pX)tQq-3{Dt5zsl&pM|=T3ye zM2TpO)=gp6N!;(KP4&>0z~!e`FwN?cp5n`WbTv+$o7YZkAXb@_6bX?3k%0HOlklya zIq!psA{~s?&vC?>j*fCEnHtTZ>HNQ{N)k%6+@!rqs4W-f#nXjdvOC1OXki(VP4`Z% zs1+ zFh6AuxTW0FoshVa{A2{v6-?rBhKCdDrSF`NqR2|;YXYpcz@7{_`MSJX#J|GeG-PKz zCMX3q1P=L(s-&i!5Z-#51@$R?vqNtLC3t!PuBcT{>7#^87`g~QqHzDF~2LL`qTxXmR1L7enri08GP}|PM?H$ z6S#$T$3YcIJog>e;y58fHLyl5+uN4-?!Ex(GUmN3TLzc<5JgI?o{PB;g^{HioCj0} z^a8N^r_oSu$88bUzCNSywwA-L{#GWur)5I08&qXNVycIv+jK_o0<5p7ZerLK0Q)Vd z+r*lYF}ryW$<-JW-r?6BOjp@jucNK5GRd03TLe}DW*G(=mn`@KjqkLi7Zvciddp>( z^E!HVV-6`(Y8Qn4nCWzRS^pA2`U_rNScq^Gn=NjiI>M{wo5>{y>iQtGYwUthDFuC?ZIxm z3FBh<{puiD!(8=UuC?}9i(>E=X4^s-9U-zPBO``G-Zv*izP5iYbgrOk=#%_e$rce# zi8AcXkLhDvw^5Qg;-9;{DJG7$xqM}?`%*podc1K~2YFd(BKE2@hKMbKP0mM#0OyK1 zST^VwKnp=i)T>P~;l)4WTq>=g4dp9qaLkUw|xKYEn)HaRUINrzU_ciz9@Q934A(aC`N` zJWN;2)A95w`3Vqa_vYi%+-DCmR&<_kV*jK9I2VJ>I$z|Bk-3AgGZJvRmf#2bqAAv{lL5Nb-!sfS@o_leme_n8Ub!Nz z<=E?%+Z1gM;R>4YZ;8Y>A?sTp z{_-hDy44GD@HG><$I$RQGpb~lFi%zKG9#_S-orR@_!dDPmH9hGmir@_DXCRJLeGw= zHLu_35~nPsWg~2KSVOmaEsH$5k$nB^uH|AiIiZ9aq~=_%XwL%bZg8An)&6z*IUUvXWIr?x)gg<^ZepR4c?E*E}ML}hRrGb%&=p}X8BvD zo4Ro(Fp&aPYV*ogNF-o-t-u}oDQgk@QcxgwYkHU@mM6p9pm1H&^1aQW%@tFid%)H5 zr>L@A)vaBoZIgKGa1OL}*c+@|{$(}xn3+obEqu9}N!pbrN{TraNsEKvKA*S(T24)% zyYm59?v<>`d&%fDS~`rX-E62M6K0OQ}&}+L+#$>4`C|k-lo?~ zki9nX`5HC#2+)EXzdQ zz|_5YuPxE<*>-jsDKGF}IN2JLWq^_%n02Mky)oXZs&-*H5;b9}RG}Z_uU+$VU5E}3 z`X=XKxJ_rk84#93)YOa?i?BI58kqaKC_bgcj}dL}GbPJ4@Q_KE8W+TtmvDqZSo*e50FuLZ-$$ zoCYFyhu7p>9}W!DwSl}BPp33)hs!$M-v{ZZgTE) z;bPfhD^6(J0QB;dR;m)pg=%3@z3%-np`;-1=s16Qo6dnT+M1TeRQ$u7kegA=d?6R0 zUArui!97?#$QKk5ry9gqsDO*+>R$(oUlbNe_HdmLtD1;>npUhS`zg`%M=#1Sia6Q2 z09ubtEdtyKbUP50B!!(21{}mFD$7V_?pux4=7D$Y^Ni;WeNT5}TgzxHA#Vpcal=T{ z+w)8?4VnR3MWc*L#upL}i}d8G6jDTHJyJ#6oOGFc(y=&l!}*P$YP))fG%_R=!WkdtK{PMC4MZ?hJg_ z#4p&OcNM!>DSO!DbjxY~rSYD8Q5aLx!pDa1FPGlPD@ZthW{;oSiU< zbvNbdeQchxC@KEn)5&VXr_ZK{xI*;4zFP^Ha-?N}qr4Jwb!?cDAJci2Utq8>&- z=t3RAWtCqZkY;EWnQ6S`cx5JaoAG`M(+o0b(-dj!#QExo;~8;NW zEKT{D#@|_9dc&^YBg~YgX`^3_&6{4Zo4=g1XYb#2uU$bf&xewjQtzOkh{!4Fn;zAx zmqW3(^SEqo?t;~bbi%W`t905{yCo;2E*T-k3>M=MTbCU%uLLp7ujodQd0tgM)P$4I z74OKB^$>5|$}eTF6C$N4Pl+M68S9r>^1f4#h$kpfRPOsEgX~(6z=fDyjlf*buc?v( z23`{R>n^FX_%aYj8!#TnM6PlJ3k+s0Dspjflnrt0VfN?S+L3BR^Y|`GN^vQY!Z95QoOf@%UlS^+STMKPPR~@soA0p~up#RalIM z;=#2Utgpg^R39sCH(daFC0n^J5Yw=)E{i_M7I5&Zba&lqJ@k%-9^XQrV{9_{9%aPb z+F|6X*`!8f-vg|sSXo(dOowML=JQ$d7x~fSa9y<`diw-@Hl&|35WTRv`n#8cKDdEi zsr}U?J<%=?)U8IZx&9Wv8Yy;E6)^JnB|Cc@wmQZb5z23jt<;F{J*|Psl$|E%+#4r) zgMYmd!j#lo=w!*nh<&mo{SQFj)+N%>&M@ym7Nf$V1=XMZ8ZAdfH5E?Zn7oT-1N1qi z4+?$|_Cbw%ls8ucYG5`3Gs3+uL_-)D>ER@}`O4Y#?;%r9L< zn=@(ZJIaqSU-mDZZb`{FGgVFh&|2XDs5{&vtg|L$?4pi9t@O3xYgY4GwdeW#aPVAB zMkpd>zz+v)%hATDC%8OhY93L0rGCJg$!aovWL>K1^#it+s0lBm)<|Z~{aHw?mcjQ& z{9%B6wDkeK^Y#xQ=LOB*sTw>#Fzv_hP3lroX;-;>54yM(&0C*N&gC>bolp`f8R?i3 zoFO7!$GB`(lBg{Bl1cy5omy^;p#u0R7>2scRIJnmLDz-;0jM1CDY05%dw$%f;$yAk z>f3zsn5vpxX|Zo<-~g>)B{yzn7_v8~$FKBr%VOdCqz8ADgW?3AB7i_YRx`f}P^%hH zw@M_|?t7n*^K;db#@Z4;6gasCkgLgiQ;V{{=W(AOr;DXoLoWa@5l!d|m&T>d=`mN(p{wb-1 zr7{2>)n)!dLUXwRsryy3aQ+{FBWI^0S%7CJk0VB?M-_3<&wXC9=A!484Lf6NDFv-uK19SLgt+s=60(g1XbH`0kO!dMmgHzLm!EV z9s|Pl1>|R@*T1>8Cz^_y*0a`(b{Br9&cYsQjn}_i&1SVqnVm6WR3z-0Zv6<(Qi&a` zAAH#STYQQ!L!+phCD^v%b$*4YoCP^WspAZ1L|-L@n|K|3)rusE!i3fKenqx0^UI4G z7iR{SEQ#V5Fk+k>;QLXRN>OK6*GuJWmKSoX*Xf)lFU3^L8I9WY%Jpw#Dq$j-)2Qd9 zep|L&b8Q{+D5xa8vRkyc_b=9qD(Bq4#&=^n`xaY)Ajn~SkRad9Chd|iyg7<)Vyevw zva!JlU&cBOMg6%Ml0oBgfx21N-i9l=EKPWp{``dhfTL8c4!?g$u%|U+A$Rmv z>puX(M;EbZ7BBpo{kmqZt37uQU;hYc~N$KTu1NRl)qnb#}ch^#!IjIRh6#fo?% z^2D5t!P>1^;m3~so?NJXav=oJs%Nz1j~pVm+U1hRt>gA8`erI;PyW$}K2aw^yl(Qj z^-aUklF+ml6CSkQo|A?F+Kv68AvG+$Az-yF04H>nj{Np~uv9HvA-@Zo8~^a0BHwEw z{1}+{@g?Z@1-8;Fj8E;nxyJ_jZzG1EU32&T^ODP|yw8cH#xYbTJ|ujB<9nF$ zulQO}tyCJo76gC5((4#rc*{^dGThR<1iLFjLJRDOtjq^~0xVMWwlo7i&oY(1=_o!X zA-}zuG#H_G!%>Zs{~!x858D=~-xT`A>$%mL_a$G>(Jm6a;F>b;Y3a~Vz9b7bH+$Es z;3a~uf$`-tOlVzy_d$c*$+r^m4&qZ0Ueh&Gd!yVpBGt@X0`24~e4TZzHia1PO-JRa zsuDJEwSS&t71h+7!}KS}`r=DOyg#dWwwoGU3DgGEt9)atFzrl(PHz`Et(+l0goiOl zEJ8f$)z`_ES*PQud#;2 z!u{w+-e&4q#5UF?6-!kBCus<7r0U=$g?NHCh)TMWtM0{%Bf8d6poT2gbDzKt&2_ z^iDPpIfns61;*Pkz-t?-QsXo2&_NAjbHB$pxqG3{S}F0gghA->a%FEK+%^Ksqu?b{ z9`MXiY$U-qDhKa4Lt+-(j5jawmKFy>SUe+?mys;8aiUjn0e)vo$8?Pr9!ApHyfbgj zWb?%5%N&364fztQU4Ml+q~h_sfbeG*#z{QTy2RTrl)qnM5v&CMp4X`JQ2qQY52JDt zecA-n4f;59eB97T_O4&{V&8!*=Fo3tdXTsT({*{-%%<)w0<2YkA}vH2m1tNa=KW(`_Rp{yxI0adw9pN3Q1D(4j4=x?G!nt+R0kAquv z=;(GszIA;T>_MC^y9lo-dH3GmXPU<`_TPmzD{GZQ91P#nfP=-&Xm_xo%3XeA9%UOs z<+trIe3LBmZn`qy=?M*Bjh$)lTCV%=6z*?jtFi{+8{&#{9TO1F4tBo#S%&Wp>IO4r z{RP}CjI%5fv*t*JLqXareqqfkHk^80KtJw_Xa}LeF_{XAn1UWRv+v5HPMMg>BWX@# z6oeXlrBq&Mt>KO&kuM-DLt@d)j}8`wxU%AjW*bHvF@COzQ*!yH~UhTl^P^WF}U+sZtsn+klw$q;`bbhHg9C+-fw`t z&aRh_V^2p{V_DR8mTjy2R&ANCH?>PmXpToxh~_LlkbTYr-o$Cd@-FvYTPfSiLh)Z2 zMAE>{@R+ENO+4-g((&*iVA1b|KALdM42TN7US>K)|FewiQ{s!?JQ z2%ctS1F`dR!lM|OK4)=YVvjwPThXc)Zx>J#Xe%kNid?rvECRLZ%lTprM5HcHYqg+SR&NC#xB-v zdSf6$kj+93^68UY&s z=Dv0%ZK8MJZ04?ikJP+y+FVljQUqKMh{xb1S~xj1nAy@NH4{?CPMNskE8L(&VAm*22rHtfu#zFaaYQKa-Z<(`Qcz35`12tu|8jPq}lQdQd_Cu z%_H;6t3fS*Ru50_#K2eG4)v2+6i8|bRd#A$jDG;ad*AOA6~CJfgzZh{qBX&~%z!$w zunddd(gm~@qu|OPRmK66uO4OS0}g+b8h_%Jf4^Yr{|E2_=Coign4Jz<;pv~FNg#J= zIf5<)xxFSKd;WRTB6WW8VeB48qkQL8)SQ8Boxv9-R5k!mG_<}=T%^XVtSUzY0jwqL zO)KpUnfQ^96a+tWZ;o=suU*6Zv^hh^VB=@m>|KQ&H2Exn=SiX8)lNX}FQ!@M+}_mT z`y>yo4IaA_hnBs4pLKPVjiG#>v3;uPYODu_1N8`2AS>Rp$u}jBWB6^oy47{;^^;GM zRxl`&2usjUd-jNwOB`Wtu_n$YHC#~n1q-&s@a4oIX5ltz9zIn@6>zp;IGnndwLq1i zNa%0)ZAh!Em40-%e82TF?%p@%`C*bOy4oEdRRU#@zmE_~u0Fv@YLtAlO*%fQ*@y~! zA^6>1n}&~vh;P<0u1}6{+|hKDTfjK;ML^=c^@J`t?Dd&|55I~wb>4zxN_ej*=$3bG z2}Q{rsB9TgmthJ(1s74OGj0R}N$6-q3pZ?9J|PBVt{)>|Q8p^o@H};xXsK+q{M$EJ z$@ck$Bzmrwxmp&?&5aUREUwEu%YSYUto1sdKKVjUO6~S~;o>r`0#jw?0Upsh((fL= zSKCwGA73|$8;!4nrp$bwZx-iQ^AmlB?z|5`3HekINuunA>|P;V9w2=eF*U@fl-U{Ja1(T9!sZ)l{-j^yKIpH#pj#0f@ z2FOhSfeIeY#}C3dc_S}e1Da|--OAY}6kGikwoKvNqdUpa6UVpkR()mZ7h6BMGB1A% zm-wglVt*~cFD|+o^t;(bSO8x=Cigu;;{#3;X6V+^Om2M*>TBp{nx}gBUiW$!Y#h7A zx;zIJ6VCl|U{p`XE{<-N{9C1P^a%^hj%f_3T1Q3#cW7yiCM?KqkQgs(u(YCX=?a_( z^o9o3%Gu<@(eBj@qYl@zJ62bNBA!y)$##@KR<^3ae7nT6IL5w)3SI(?!1gf5HBowR zT$kKtSLmc`tpXn`0O9k9HX4=ZY$^5`IFVjtH$=oV^ejzO^!LTzty+gcc!84^j!xG- zJR0~1&*#n$ltU-B;%&4u4Wd(}dhA$+Nbb`o-w&w9XrxJ%5l)CKuKCrYV1Em*Rs60o zE6Q0VVuI|Nr`2bmUb7%ePXuA}#l6<8*?GfB9bJtthCq}zBek@$0W$8%g}jy+yiJeD2RNXJNXpLIbD1)kHJv5fJqB<{=Aj3Sp?IjVR! z7dOOpV7`LyPXGWGC2(i!_Kx?DU%zk{kKFe4#Py6s7KKu{mVh*G=&a@}(q)Z#!Vhc}Jp{M{0G%@xH9qI{8SHlB;lk$DV#pOdnWjLT2E?O7ZRhlmY5HXatMVhCKS z^Ui&|*SkNmi_6|x8m(L9an_3)TFsT%hJzF z4YyOT&(MUK%x1#%)q0p3%@={XymJ1D%&~zp^p$kql}y(NN|*|iQ>yvoPve(Ndu8KK zw0phzwp^Z{y8$udYC;dTx)@%Qh>(p|)zpLzSW`bsx2AETCnl@StqSSOG~?T=>dn+M z4~5g)7DcF5T~rN(VbtX8p$OZ<^n2_DGAFpNjZ}M5ZRZj<&6;(w*ly!)vNlU<7HU4^ z#nC>+LuhR{;>Ob-_Wl^dG-{{r8Qa~n#A3G@593#H{K)N>F!aTQhsZu!d)A~B=Syi! z!r2_((Bm1F2)bc7chJN`R}=d+%OMvQmpsl-);-mcda?>mNu`@vkjjW@yT6&7ZB+Z#bl_itV2w16s?ZziC6_)*2^;3Rv;HKR(xPeV z0+#XTshFz{ra|L<*vam!$qMFK=f9+H;=h|cr)csNcpJQU-`6z_KG*};B0ccryY|21 zAGBEBfKe?&A`%s=-N$R7+D%#-o)nY>@i!+hA@JA8*fn8ioYc^kn82H}r}vqhJLKNyKNW z@LKwA<0~n4CBI)K6q%f68=S1_RaKQO)vvRzKox7vl0BT0WB^;Z*ce>^Et!N9L-}ns zj298d@M(ld7LT6a?tIXRp9JUeK(Cjzx+F#2Mk!b$ssv!ycattG6hHHn(tu=Yveut8 zpo;}TU0W3?yhdNUtKlUIj;`X^>jAmEm#6$S7wu|=jT ze4d*zVYbDA0MOT1e&>blJY5H>E;j7Qc(bkfx~k5JV!qD zBgrnK`Lf0)W!nsR^+bYX44A0syy(Cr5l~!2FMO02gX1WhzMgePAAk|Ib;;bsm6v%V z!x^nkZJpH|0|2HVmtT9t2<1Z;qCOIjab+v-gl-xq_HZlSbYi(D$#aqJ$gt7_jslIR zgA|6~?dcO~3gTR^DQ!uf(`OuMmj|1wc-&^sHcKSaQZeDIumaqkGL^@UkG0||zm+F> z;1%>T&xAGq=-0MA7~@z1>|X3@qm;jOZ;DzU`=#-kn8kz2XErE#i$3WGI8%W=QYvN% za^G5xh8xoRYJB2~e`wNNb>~!N)>}5|ExryC>xviXobWU4+dN0Fq#bW!7gDOigJO1S zt9V3+Gh_MzGL^1AU4t~q>0aD}qvFqP@k@`=okE*q&8VKOG ze#kD#EbvcYPFgGZxb_5TK+YuVVLO36A4v&F@kJ%vtpt?)x1^{VoOs}Q zJl4;*Ch+-)%U}I$6$Qz~spgddbKQB?w2n8e%!WKDH=<&lQ?m-bJ< z{f%vu)daWT-Kk`=-a*A-+|D6|AW| zSvB8^D2j^Ql!|c#?Xv(3t$X)|BDoo5SOB3OPvBR1Ex_exCYi3^I-PrK_UyhF*4`~U z)6nzzs{t6=N_n^UZ^HP)tM~{%%4%wQm$EJWbdSZIObJO#m;EShQ-4u zSzkiR63vd!y@lC9WxE&C@t^(yFqM3n;;ozPZbZU{lIq+lCdZ$_1Ixt;m>3;q4Ic;Z zkj%)GI05M2caas?SbPZlTk6zDB`@N~zrM0cj=2{5jf>tq+!y&A==k-CS@3r+Gc&*Q zu1$)DrH5f5{mn>>jk#{)A+v%>BX9Vw!VmWKit1yjC2J!`P+Qw3XhnYJb#(Ff*LP}A z?8u|*F!{``jr>{n1A~4v*24wMcz|wh~W-iMA;Zz(m`UCquL|GD{!y2^1>R2Ul64tVSiziq+c%oX<91En-ZDqg~OjHYxdvHp0cTmE5 z2HFH;33mXKW7BGr0Ez%u+Hs8DNS2Fn7R+*}H!0Cw?*Bd972fARB6p|%5*GX?5^OU` z779eeKv0_8TM1++O^G25F#8NtBoXA348Hh}gs52w>p0qrIOt_4H{}?(X&Ud6gv>Ak zDiWVAqM49V@h>8v!A3~`WHAYDoy0?pGomvRnPj}21z+MaqMnHQD3C3VbD&Fw4)fUD zC{0n+40J?86h#CbA%^Y}gLaceQQ>z4Y#L!~8Xk;7Th|H&K>GC0!Ga;q9&nD|D{I%O8BSY?g-U73%$#C z9G#r;a)j}3A^+R;d{zPz322V|A6)VOg}HeUv=z$OGW!1u_;!}o2aV*d%-!|>7XAVJ z1Niv|>gVq4&k@o`cM*^~a{Ucp^q;25fBy&HG3zCon@7-BqB|aq{&$1@M`qKaC^Uae zLq}Ahn559O2`EYd+N2KNnPFT^{tvLfg`0$;(2%Mnm&3FdZzNi$@oZwi#R%|uJS|FDKn_=e1zVJI{J#UE(A_T_wD_kZ{l%j&3tIex zaO=OKy?Z*1MhkA8h3=|zP)9{~F^x(*+!nYC`kGe~tfNZy_U3B+ym9 zApIW&{tqM=l|>W)f**8GB*E4{1$2xx{x{(BNwD`Uk=Hm>=tRP29E@f@bO9Fct`ex} zzy10DWu0A3iA7D(#RSOZ6N-=|iIHj1okr0-=Rfc>ki{7^1w{X(y+fj1f=8}KKN3zR z|FHO@rYvNdMNbA(B~-D55Iq7`sBxsm@nlY%Nh}gWM)1KL8Z;x`ShXY)i|9_MF1o7@ znVSYH%t-7eNtBL}r2Q$+dEFh_ixGP5215A(iC{ueDYII?%{W-( z?U8~8bTOINbA%D1z07wMUnUz2X05KRf!P_{&*ox+aX=wAT=i~COp1P>~_r0 zIpt!AL=IC!?M@%H)POaZv)PDWp|(P=tdXk+L6(+*{n%FztcAy-zAH`-_ds0r&!K8G z474*@Z5*XL(yk&xW~3kb2?MFq`_)QL9uvLnNWZR2>+g`0!LxZ*E`1U1{UC}Abby<^ z3N7goG!yA+^$FNKmYvabO->+0 z9N%fH-;>Z>=(DL(y@h1z*8wzrILOiSs5|;#+|8x#K0vf@p8uc^;xBic=OHVIP^LTZx?bSn-LK*3)PQQQ!yfEhA3Zb(LY6 zHReUTqp+|2#|YeR4Kk3>w;UT<+N25QHbQ!qKrd7mU>1u%N>-8HH<+DY_q|XC^zp<) z;gUWWZ=I8VmO3Ync!e+f>6Y4CO3ZK7PDF!0@qizi?HzR5;3nC)F3Gk(_VstE_he%f zMeN}WFdn|xYq8!nd(65`i@Tw!vodVck-)zT|e!=gitK~>`Bp*+Qe%yV5rZYbo0PO$? z3kwI$Y5(FxG#?7dk)fR+g`!;`v7lWbZJ*0pUcfyO|8|0e=1BB0|0Pzuh}2NF7K2;Q zDTH1q0#I9jsHGw&!2+hymB|JoPuYgYBOhCb5rsc>)aMVYjxi$mn8?BF=zO^2ddMJk z%_&RYK6qojJ}A5-DR{|&_$Ux|PHBqm#pSAXO`{V~@JY)dVkrf|rJ5oh2G2Udlizs* z0U(@8f+9*%R8Fw?@U`3}A3>dRg&9Uzgxxu*>*GReM2ItVYlyUj8%xV6K0HjRIkuH1 z1|q&qRz}lm5r?yQZb#T8=JE{#(v($#=cIrn=Mnd7(8t8Qa6P-G-HXTs9;gc`+7hN8 zPqAWr!W%s=n_;uS^4TY_>+%Ya^OO^&HkLIq9qk<0b}bO{IV93nvwl z*80!?GaDB0Q4>DG%utR(*rUGE*##P){| zpG`KE5E2L>K)?`+As~jPfT)|$18V3+)PR79Eff{|CIkpY2?&Ua8mcrE5WHBfhKPz- zE-ET|(F=%**sxyP_51RBXWn`L`t9tTo!Ob)nRDjMoX_X^e4gjwq14(>!16PH0p;YPV%fOLyevq!Cmnzi`&q3h_0K z=u5|iM(c#vaL0ILXjvyPt)ix`9OE}!-${J0Nlq^ta7^TPuf9^n;F#aIB_+@MAMm~v zWtRMWl$GJ=(P1=dDMqz>iUKGig~~H{r8MvIjoFwKQ3so4=N;^Jd%T@_PoG>jpKRgK zu^O+dcpJ>=14_YAFU zQI0cKS9H$D>O`JiiZAnz)&gboQD$MPAM@m7Y7$B1_}|6|@UjPQ27BUqh)l&F!|^HX z%k%e}<$%A-~1oZ}vw#n#m@PI$*G~-{= zm?9v{EG+khSJ=fQLYo>p)=sU*TlV=nM)%Fhzs+M|j~SA`x`{R@RZnwl2)2 zz1k&(zb*RFs*M-u-;^o)nUxj+fKEuw9VPBVJ%TXQl)aCctc(cgEP zj})9;qSdyL-xS-N&u9&+5N)Wfe~mihxQZn=-q?gqT;@)nYHjK`u18d)Asrd*T4c3Y zKf6}t{k{$mvphRwP~>F_^yV#<<4b(_IkhE%l=u%z;dG8<6WG+B zRsuS#lE+m|v#)EpyaRqHNESv@-_}7m3;h1MH zqlTm`R1zj`FiF31nF(#9sF&ZC*rvLG7cnoT{=P~*u(r*xeyK2!E-~7iw>y}x$JU*QQ8Y4doovc~}YoYz8Qttq>ckZKX< zH9KBJ`-7ao(!kju-ghWQdB9jOO}ZtosWxR)ne0fv>fhS)k6HsJoTwA3luCSoon!=@ z{od?oeJ2Ku{j;A+2sB7woqMw3-XKsUdV~pu}&Obuq=ly$@65Bn? zc~S@GxtnP9^!zSSCT`Z%YOPheGZ7&>^ar6Q1PoKNKc{?OczwEy?4bDv=03xI+m3sb zG>9f%z`hOi{7Anh_HbWL{S1T8DS5!mSjk&64@*5Q6cH&^@+hJ+!={XJ?wO|8 z3Gd%BMVJq12W1T6rqA%eqiE>Aa49%rd*?Z9ahPm-WR%4AaV8TU`clYy4Bi68*`5gD zFR5r#4zP9hJN4o!<wA!yPgvY1hPm)B6~XWyz%pBm zAFSk1{#(*Ffkp<&saNfd)tr0x_DSyU_1!*4$xkxEsF{nn>xKKFoVZh_YPTwsWtur1 zfHs%PSJdfW9_^7wo;v80=RxF|S>BBg^o+}<2Gd>tt}7arCpa{h8I zvfa+Q2{m0U96rNbUZ~v>rAZ9BUX8)nY%nkI5j6rMuzR%am!=r=i6@zO3h%Ekn>HNi z55~N?%Ibg)i)qjC4k?s;HvuD7inUvpxq}B5)yl#M4hIlm@-f`nWn+p04KL zUc`L32m``j@AX;i6Te9JrB8^$<&AA!Mg^{hilz)wKMPYs^him-YNG!3D?a$*qqbE} zBCuzXsWq#*fyMj;OM9CwoCW`Jd^`;G<17CMC`QlV-=O3#5b$OcP@tYE8%M~U)8xnB zR?P9;B@Ypm<}HagAL^84+bQ?NWVhdZ=})!@T<;tl8S>%Z|Abm2?ELDvUM>^K!m@cnFnN&MDE^t`YXbU$m9m@37J zSMUdq5zf5$bb-lF4c&B{BJ*g-$`Ks+4)VhAc=)Q)4$%128Iv}-ZCxQzZ1mD8HWd9bhg>D+tJN4BT4LSC_p7!i)iscnfmqS`GC zC$in3FqN~Ft@Phey-b08VLw9;9T+#gJx2ysw(7;>e4fuE3v;fMIV-Em6s>E~U!4-MQdnBo(Sol<94I$AU6&|Jif2ibg!?^3;eTX}kmt7hWu$cLYVMO3^~-;q zlWXVY(Ruf$Qz;1xCj~t?=vcyVw3U%xi^VG*1$`Y@i`r-QI^pMBQ?cBk-m^lJ6r6lwu0M?-bnDJfU~OS4LTGa#Y6hsha{2)d2XkTxd1KybxS)bcM7&yc zTD}AArEjg;nlvXPtA`Z7sp{LWxiYF{Pkjtm#<1SQ;YnSV&8Z<}$X1R{O=IW6y?RxV zuA~ThM3x)vCMiL>38GBdH=G9&TiyE->}|z!%9>8}aGpy&OfERMWc4nsBJo5@0J>+t z2V6;)*%0Aj%xf8WE2g_&dXHvStA4AUH}8#LUN$jQa1gZ~OTejQt0R!1uuFRxlicCm zr5p+8K)LNrBQ(zNXJZ4jQD)rkx}Uiw3sB!5assZ8y~z7%D~R%6TtA^R5qD`ed_1G1 z?m5yF^gd$D%IV6#<9uw0X9W4pIOtp+f;iVoR_`~UklUVF>C+a#YjY+KYp9I-+(oaX zIqnAmBumZ_>#ZC(4KR`*&LB=01&et{7l!+#Iueb~l!szw=>JH;OW~?uot|`OouiKg z_;#MqorZhN!;r74s+Ac3Jh?$by0gY=>y{bi#ctBPUxEAEf3#dD=+Tt&K|x)c>xB=! zO+3rdpqY|rw!*Sao8yVQC@PC0GnmgOdcT*_ur{4F2jdIgZqAm?`ETuS(ZPp(S7JSS z7b&d#5<=YBrd@uB9i+hN_pey$%jyCL*6-svpDa|ngclUoRXq{cDbV?4SIfn2)3m1R zRy@`s>0@iwsl+@mrFV3!bBbT{5?D;${BhxjnEL$= zMVh@+cE?w<@^LG9e~4vU(j-!ws?Q$$AssACt=!p8z&HiQ+xN|mOfS0Abp0gGOFp{L zBaUI-1=C)Trf?7<=@PadX>ZqiC)CHfSIJxRU1(wI+qp3)XLM=a+MUBcObshQzh1Ej zj-{0yauyEEQ_ikp%G9~nJ=~yz{&W%#wo3bP+^0TOj@zEL2-FrprUc6NWUKmb3}n_2 z34@cyI3>AWn#OcvQfJ@F!7+&Q(?p&6)9T++LX$CEOInF@5naDMi-La{YIvubb&%pE zoD{%~Jb|H8C?7?QU9uHcL+b?&vi}032Fthj>)(Ln4sGonPj>j`R=GFo5T08c&ycd0 zmKro;V(eBnx;0N5#)jr%G#vHXmq4Ypu~(aTi85Qz2+J1wDjpuiQu|u_eDtn1NC8nX zQ;`I2qzatQ`86x%s=k$?59Pd6fk?+0&($^P|A#t#(&sCB{UzJ{B$z8=;NdLM-hQz% zfWx6KCc1flaq$)`8d|_enfKpL6rNE!^!-2B%Br?LID;}cmdvr3^;edSSg>ecXOL{! zk2~zZ7^!ZCGnnLltCVRFBIZc{WmxH|BCj8MWh;s&$f%KUPVC$=Y=H- z8vfcNy6kJsf{Wix72e(ud5`E@Vv%o@=Iv}bCZ zufjl~vmOySX?oI-yhP!9^=l}g4HlxNIfH$rtAJkwFzlWE3;gT@xnF9-!K8^YWHMrSPRqSfq|h{aCP4RhGCW&mW(}B7-NGGhN~Oq zrHE>WGQk!PEI95}YWQeCL0+5D7ET%qs=i}WV>cWr{;#m1PZSqKvL6)FQYXWE4;6f= zk#=I1(Q&;?XVZ8zMI?^jc00YlJ;z`8wqVyIFjCHmt(7in2z zj5%b{yC=&L2{B=m8}-3GleD}LyugEG=$V`C`_A)`2PO89o-4!K7cECOsYolV0iscC z=3X~mfZLGKR1U>!_2Ts(B4W5`XbN^xSLhpTbMZ6TGRiG$P$VUvSYx6u36Fi>JhESJ zm7lA)T}+!^S6I6V(Wt5RHxM*3WtkQent6lZWeA)SpR-j7vLE=*$OYemx&Mw2Xsc%Rn=FQ)+-9eYyjJWevsD z3JjDIVd9q5a5YF69X3lY>w^7f4nL$%(UcythtV2+5<4v3YIAZJR}?<4=`n0p{PeUr$4hpIaA{&kYyL z&79rOO)=mb!Fd=FpJX53a#WMQLW6*w%N*FL??vYOm>M9GV1+2%Ydk>( z56N7xVF^q`G&)UjvU+S0C}O#r&|TgE)NiDMMp@-DOt5Pjs`KyM(O=ackW`clz7Wr@ z92Mbu`Floy)s%du#c%Z#Z5MG1Hz`Q`(Y2-&?fMpu4{`jCppK)di!b+`)(&r4Du~Hj zZzk!J4|&t}xBBXZl#X9^E&EC{52y5*Lpyq)y(-sFp&a&ULt7_yB@Ny@_5)dTzYNJJ zJNqutL@BEsojodC6k{|&`UovAb&ynGcA?KKcc>$W zvLH8(NKBEV7`gNaM?9Ukp`+u!_Q3Eq|9*J(I?A4yHO<8fa@5<81hc{1P7hM66kN%- zZc(KuM_J53TV)$K8E+7sVqMvBQEL&?)xgFSGSkB9TPc%q%PRvr|CCc^5yiXA@3~|9Xu~4RzJ**Ls4WaBdF%rZnvdBq3$D`` zwNaa;ScT;Cg~KL?F&HUg_svSKArpzY(y#-ZN{ip$z3H*emn-?4jfrdft|Mo9IW%6X zj%U3vRWK2_Zf_ylzj3zOV-a*R>*4G-btcmSDKWH+mbySdS`NoY@Ws!0b-~t>x%0U2 z;`)$mF!BoqKaTnnv9sniUDd{_((7~Lvwi`~8RY>ix$y!Wd=45#y^d;1tbR%kj)We6 zX!fLc*vK0#8yO`eF8j@-0g!%;ojy}Nn;HM_cV=bA?bsWhYaGADywqKrc|Jd;$9`UY z`u}~s__uY(|NYbd|F2pg8|&L&{6C;zFPoCWw?ri{hwvzo%IcSLjSzTU1=YJ9%)6Cp zb@VRA46G>}C1h#cs-T($8jZK255(dFUe$CW=}pp;0AQaC_eks|HBYIG zDg(dvrYZ-z_K+*CR!?xE^o~h$@jOx=G`p!`)N9X>0Y3ibS524}Gwixmd9v$xRD-jssVK5N31hCHkBTm_X4LK*sX*4ulk0MdEYEYi=59pY?J4 zR=sj@ttBn|rg6qxiW|K#+_e|pnR?*f=g>6AcM++chp!Tqz@+tg$=!(4VzLVXeNZ&g zfWUv3;U=?f2LBbCzu@Tk)}r|8805{T;wv~@0wT2@AsqnI!(o9h) zyhQLI7K`pg89kmz{_qUl;eyLjZ?z#N@S|_JnE0&`!VE4y(8vumxHhGRuqSTL&W^3< z`qVxvtWe^V&)beS5S}!`(WV<`8qX|pIwBm^u`p6b{v$j=5Ol0pw{cXxC$%PoQbEIL z)_P%qOWD=p95E7FCP!xla9rfmv4}mVY9py;xL)Oi-$pNgm0T#7&qlbw?8YbS`Q~*w zbf}5QB`YF0rAio$MEl(78ZxL(>2yHH)}5s+&13BN+jEkpvUg5?kTid*ARqBMr@%U@ zI6()=&m#QVjq&$3x0~j`w`;5~`e11>$xPl;jWp*fC%zfKyw_v+<>)dTcxaukCr@(= zr=m<{=dyk*w_=`^)<KgeIvAa+|vLfuYC&xmrZ@BT~gj4?$%|F z)jcuAheE>#A?FfK+pd}{^E5+uv?{R$UjrhGGkg7@|Xanc3`xeICB7^jOKuF6N+SXTOY`A99a~od`g2 zYo9=JGngYYZ$sKh)A;os$8K(sevv!_Z=pVD@62*Dhag{X7}eRMPdC zs=RS!aPTtb9R~E@+@+Xw4F3t8(~JKNhyMO-;KIt8)&KXz!YeWA-GKdJucOtg3lQ}3 z5y+T*9Wdez#}DT{fir8f(8yI<0N#H=-rIop-w*&W%ihh5v2!ad5Y*Y*7=0duoF4(S z>n~RPaonWG>hJFAbGZ~V|BNpNLQmxHrsOlr%)oQqWWwi`4C5|^vN2<&S-vSQSBsHg zrbk6@hz;NVi52B^xRC8FD&-3LrwBH!W@T!;n)dT*zm~muzM0eUH;o3KAKy_nXmSri zCmnr-DSx7LKX#N!4$Zj!$2MA4a_Rk|6DlZKj^VPF z7PACA>rJ^Lv<>TqB;{o3p%Lt5o(f5T|21oLvfPS>|%RPP`{fB#nHA#Yaf zOx&{vdnp?L8Vq>t3J0NlOj&K%HxL>q!*a_rlrjU_vs^6WX?m*<6<5;y9DdG)I7sQ@AvQh z&f{u&i9^Q3dA*rVOJ`4Pr<7fGYEvuU^lrlv^eaarP80>!_8My!IVKEaa*%>ucu!23 zZ0{A=*EWD`W(gv)9t3k6X1}@?NbGy-f10CkBQDwEkqq*J>#f);(iBhMJx|Sh_F*v+ zGUTTp7&thE`r3!;Y|DJXZ{M&m`LzXY;WW$>Wj(DSbt%=RX+?{gX5CoP9^ZY})ZSp{ zw6Aq__PK|}w*J)v410w3p$T`!;GB|^1}`UeKiL>>^gIzFM7n#ZC%5LV@clQ&Qchg0 z7<<7C-@iiP({!cCc))4;0+7|aM?@AxOlh_R&t}vz<)l8xE0%TT2^gX&*4`{#R1+9G zxTB{Gxv5^uaz{Lj?|?%bcB6C%#Y{f7H1?w~ycK6h2-qPXyi24!NS~H5iqfEE7q9Y7 zk547$^aE>Jab#7T2hI-ItLlZ?6Y|_8U^Cug6aX6|%xPdIdQE(U8vYNosZkJ64jahr zq%~Rv@#F12FUvpCqcKX$PuiKdI(ax!+5ozLt&SYnO}O zd3p+$N%C(hg?FIZgWmXs?H-cYAuIdOyyj8R@Y;iySfHvjglO*vYh%=ra#vUyi>s)7 z@o#G5s43ep+ml###9-+(MUo^ltk1uUnA{-!-s@-3g4Cp!FD%B|dA$)681>i-^f{Bx z@a`o7QtcB2`}EFiBwtCRNQF9D&$7C9rV&*c%OTG=F@a~x(P*rCW}}Ss)-gx-aIvwVAe%7^|s|C?>A1#kABhQ zP$mqRAECDaeum=ij7f8#sfB!XAYQeGCZO9${^htC#}G_OS@6SiT=34AZ#(X|!p}Iy zC6&?az>1$EWTT`|T0$ST^t+GBug^)i@|mv1WuHDcsFUpm zgU9E?$RBUxKbo~x;9mR>WYl)k4F#nc2_R$TOW1nftx^8unzhD*Pu%_?>_J)zH2<)nIcaDh&ZG2CxiwZR1Ks{O;^{PxB4^tCf?hlOC2IPY0}L=>~ByIBqlbi4coLCB?RjE%tB)DNcv+qT(~IM0z1=R__4qe=S1 z1Hi8_fX4T9m#00}4Y=iRSMKI?QyuF=Xik=IXPQIzMVXNGTe$gP%SH+nJR zN3lGxR|w*v2n&saHQ%mDj1jf*;wUw*0vlLRyysGv05!=)a+BtyYJs+=#)M%@7db(` z@wJL_J+N#bjYv%0O4K{3B7k*G%SMn<{lZc6tyF$mDWtt*?YkbE-j4@*tI6}`hz5D- zm$CY&$!1jyAgfQPvp@*`x3!u#uQhm9Inw<8b076_zi)(QdG<3olDg!7Ad6V*6trI+ zP#(7Cjxno6j(yC-m@##d%3hHm%!L@Txo{rLK6lT{bh4tWKU@f(p>Q;u3yKZq*ANEM zHsA|yI7-ek>dGZaZUtia$oe>bIRn|u)yHBCJ#Q>Vvx+!16qTsPF#@^R2-?E3E0(|* zDCbq6L{HU^%@w^{L_<5V!Sp12?d1bXh`xDQ==(|E^Vc4-8<1;Xjulkuxu+65jjxy{ zDHCpUoC5;R5u3*BuKMCx7so!>kcWe=<1G6|_eONZxLWeciQ`i(nnWwj-At@aZ}k?t zf<VT&- z)YrLdbxv+Z_d-@oGrJ6Xh88r*&9Y@I9q0$mI#8$!tvUBT2*muUh+71`?Ij0bQ#?r( z&2>uY$|f&fxUox6R?mrzv1_H2ITNTbG!t2{fI{4g@WLc2^(nji`A7|>#MJ(ML+@D^ z*N%zT2hAVG7k$@?rdLbxE9QJ(z2fwO9hVN@dh*}OEZg!&RUY*d^Lc#sffs5 z0G|!aj%H&l5r8=c2z$<8(3ymZ<5=6|?+sEVV$|whY)Ch1I$d8FsW+3{G*SY$MjGv? zBA#(NL>{zRA)}YoH0&gwp#4bMC^3n7TB(f7>)$q|Es|mAy#5yG5a~UKY1m_%QtBfN z_ZjcQDQVT?Cv>TmL0gsS2vJhg=1x6t-O$7E3aC9>jA4(hG;HTa=2glP`*HlMq1hCL zPgTbbGX_)b`D0o^daSq>l+;uzO(y;Qa=6g5`xN0m^4ChyF3bkyUK-{RQ+&J3HslEQ zWNnI-;=Y$E&geGx!|ArdVC307XsrM6Cjzh0RCjy^_6UzErgrxc)G{k19x9%YYT-yhusCtcR<>Wj7_tK zv9+wplnZGz&&tw*)5*_!*b8Tcsb_~?&?nf`js&G$?yZ_7t-qoJ+v~$Lp{EVQ}2D1r2Frfq3MnnF6L9kK|oX zb~m2Ygkityk5xRN;izhL}S6;jMStXoTB<@m)}-`l&jLp<#}2U#>SnLhKH*A z|Jg=%9^LyaQW86t>1zaKg&=dlt;@;<;Ke5S02qJmxHH(j5cwgktVfcEr~>@JcagQC z@ao`57)XQG;~^6`D;mpV?*FnG7gVJ6e9$gU78_#SvrlP za~ZK&PiZI<(g)LMxkwVK6eGsp-;!g&!;vP?oNS936`Fdk_dkzxQ&&JF@`C0W3m2g> zNkB2S1rWw>t%kAvfHjO;f;0JS;)}uU^ZZ?{!c2=hUSJ9zb%drSPP*KM6Z8c!JTqQI zL6pB(nx)qp>MJe^pZXY6ALT#p$L&XRw+p2W*{;|P40BWf_W9N1Si3K#fubw!4) zNqHUHOGw%}JHT@=#P4g2$A@)s$C`b6yrO^{rLDWHcy@|*@|fFix-l~^xjYcl@Cz1& z!CB4*KH265+cHrD3MlY_t~i`Sg2}S zDs9)@NmsYEHPu4C{$FQeERo;O7z%RKJx$?;NR_*H8WR7t)k+HgnzXEeUk^*ipH=Hw zlI=~q1PLVLLGUEW90q1NYiw4uy#Xryz*+Om> ztz$vyuv)%aOVJt@Vl32hkSSE1kE#R6s^%g9dZnMteZwy4#@ zPdJN!PTO)6-7!gQ5I_`66?a?bj^XD?`mVnARYo&-MR@tP^*HRg%}HTfR@n+!1SyV2 z?G2+39NcE@3Wepc-OW0cfkP6z5VqkUqg(1Dl%%w{DP9;JXn*pWV;DGA_r61Hp|XGC zY=v^NNN=&QNBnZ%KBQ?`0d+QXa7oKJp$bCBfcF=9?I3rq-T%N3GiqvZaXu2#levMK zJKDVJ9fGrnZ_+zSf4-uq#pkMJy#Ogf8vpRA%*?`Wr0&$#E~m>gc^x@Q^ZUqJ zI;dAeVOe?RhjBppUAEO{h&zF+97GIdXQ%7t|NGg{7;eIiex=yw(S@{{6_o_%z~!XQ zgg|WZpfnr-#0lU}35;4Kg zV-Sq4<4Q%(a&8kN{YL0j?u>TYzQ-JF#a*|2x;Y*5huPr?DF&leduxn6YeJa`Nsf^* z&GZ}oY5Pht>965Q;J*XBKYNU zz{YKuIyj=fyTPwfbm6kh5#SM{Q;ssxbqx8Z^BqxrjoEa+yPoFHKz}1t+@8uzUzVlp z1(y55g&eg*gXjPa^X7|hGb3co*;EKtu6XKcTv3B-YN~hJ?8l4$IOVN@hdNODwd=VL znYlgqI;`07J6`P(Z@4M@Hl^K>674lbD!LVq@E+f$E)o?O&DM^G3~ z<+s>K!#H2G8tH`NIx(#!^-O!@(HeG9y^Gf2FVB(dASb*Rr+!DMG_3rm^5vb|+3E=+ zybKM`!kn*Vyf{`R{M*bgeW~@~tKxA+l!Mu8X8nQC&+&c`J|BnC6w18FO%3hE^txnH z5^}@Yw6{?|LSP`^2I9)uv>9X}_)IC=CM&!$RUPz<%&4mEJwjuQ7zV_pGOhkKbj255 zZZj^C=x=p7R3~3>P|vK<;))iJ+P5N*WWgUU*X81UviJFEP=1UvF)$`#mMmElvd;xJ zc3P{?`jDr(R7F?vTWT9~Zk7t-v4cI%NMvYU2iZlkJl{_T>+o9pJVe^!XD50;j*gQT z^ch2!-SthCUT=kn^3rfg6XsPvdLqn4pL=YQKrZ~78o3-WT>-29`SEZy-7 z;F={E65z3;2E&;}TychjG2Z8p_zdP?`>lk?qbyvU*Fjb6)enK`u}Q9*T(4kz=Qj1< zsHJcJnZYsCD?Bln(A512iD~%pk<)j}+T+g$$hpDfOGQD<_8?F7n#C&@TE04tPUfH` z-m`S`RgARac*VPWQM&7fEp(d*?QtZyaC6JEuoIBOw^|0k2-fB`a&8-mBznbsg+b^M zXJaNM&lX?%!tm8Ny?Fwi&CJFv`#6L60F3x`M^fN59MdimwpjY*IK@6VxHvvv-83P2 zvBD6)^}Anm;L8I>?Za8Tv(MXUCz#zFZsDm>0hIBCM*bFPRT}ITkr12jqY6!4(I0?j z_8zyrA{6lKT)j8<9pMsj#Zt_D%i_G1DY?;YlD0O}+imS_noZ2hp_=ayPsyn}md7@3 zE$j+b#}|Rp2>0z91=*amnrgQA`$~P3{x#u-2$Szb42nzxn9QL_oXLQN>NxeY7d)I)4%>an4ED2bx|DF?KzI~DbR%`kQU&vka~zS)>=N|`V* zVby&kilikRigXGVh)a1;glT*mIIbqzA#3)*X>&ZBBIAzOV|EPrmic70a%*P7vj#%F z(^}V)mx~{CHS7b}cxl8@T0;EL2cQX2@^z<*v+<2Hc|Sl*sEyZ6Vne;tOTKoqJD-)N z!?Snsqd9s{Q(~keueVZjIA$Z~;@l6S=b5m#u&r-T+WoZiP`s0y%i4*vrVu{+jw95} zX&Ar$L6kh$6<3A1u&`pDH2s{F^l|RtOE?kdzr3EAx#R^bc}V1#?>`|h^tz?XsXw%> zGD7TENAfN67Z-DTw&Jb?MgKvT6feXo3~QO%Zjt2P=%5$y+#k$j?9 z5ho;grHV#^^pe)HBPEB1qYV9UB76HlBP^X3-oT zpdaOBYgjrl#e9ywPzdu1z9OPoPZnpHjR&Y|mfrF^SgfyYdrS<8^)bvUYx3gOIz^Jl51BPTdGsWpr%F)M4wI6`qZ5d3cfJWMBF|ud%+#XJs>4?v*Yk0RCr01~ zvnr5&+^6*72`;YX1`}`JoSD(0RZZOIcdpGkh^tX#`rP#9oaAjmt<_W~j~Hp07hiW= z&OV#`4oD!Dfoj=$%pdCpMFGLpJEhcR6w#&|_VZ=o!q&U4ZJw&>#tRZ8H9J>>I6wu^ zYCXH@;x|Ksd|7x)Ui2>{NpQM$sePrgGB|(08B0gO{GXGy_&lqXCyhHCno@~ZHj~wf zm?x)_dD(!&YDov|)^=bw+N&R68{&h=NYAv`q>@WF{8K_{q^6595HJ@<3+!GaU(w`f z7p#sY7S^BM{4}tbT`swibjRs9vrk|Ych+j-aXE>u_M49r^rSruNcgl;Q|`Bo!}Bwj z)|V=9f#_(UG{n0O>2 zAEo4cDLgCg%3mN%;Ph9pVh#C9oh{?Vk81$d*v)q&1()(}THC}UByWKbg+n-Z4K0sP zucF`d#fzc=>I+OQ@QNHC}H>tddqL;wUs(N&5F-2Wgegv z&aQ{28nlZlPar-Ul9m`|^bXX7oX-hWECb`7HHWh(y7}66<$UszwB$_8o(-wJsl;X; zQ4)*f16kRtbkx{B6cXkxycRM?zU(Sm1%9+!B=jh@)_=YW&Du(PVLa=0NQK-;MABx_ z1ugZc;XiktK_kJVm~+p3

?&?GbZAq`gW<6F||JNGQdu}=Jq_qEmafD1aLufwTtfCoY#3cB0)qAuf7Fs+R59=P)IQy6pvJij`QP9SJJPNk~0ox3!>hlzg~Vck@hs z=%ArMI+>%;&-y&9IPI8(<5&nk_r}wP>;e`XE#sM$cNOBbi+tVdp*e5jVJ6zsb{r9& z*#ZcNmuW$%(g@27W5^`MdZxn*M-)1849itt@bm8$U@;O6Vord{S z6wJG1Nw*d@7#GlSnNglWNQZZ;p=oYY{l>HQ#ioni0$25BdD? zm;V#pT*a?%7VsA1`c&PRzereo-EwKuqDsM&;HLN9whUpW4u5SN(7?Vh!>X$|d08-L zxmE|gtc6i*_;u(*tPJEo2Oz}8Y#%3NG_3G|@B|jmzH`Gt#->*^8=JMd6rd%&0`*J> z-#V8je$A`}s7>xac!|+mo_zp}D;x2#z3#}l^Qtt`tlkhVD=HeH4Y~xEhZGnY%QKXo ztM_rcNttoMre-g+@^z^+(~5D*1g+Ln|0wQ8n{b|TJ)IufYq$GQ67>T4ZJAY7#~d;g zt2vr$5H&X<;~#uWL!XrVFy{ zwbt8@hD^*v3Zll+@*!(mIfi-6#*|VPM@4_%fUq_@8_5?^SN77%d0riX87`yUY2=)W zl3cou)55{8F36w*$j%X-XN$I`do2gvZnYkOs(t0g?Bel-LU1I6qxa!_VL!4xVzY{d zed!@fX=CGl($h@LGOlk9LV|{7;q!7${cfkMZ`(X)WF(wJ{?Zg)6~f@Man;Iu$Q-9N zA^Dh)73AE6=fp&AYDMOKP5Hjl#&g|V!Q|d6ecJ}~me725%~rBO;hflS?Lxogf0>S; zk;WYKi44n)8XrVomyPzlw%wP5TsZD#MJj}zaN;%d9s3n`(ycaqWIHAqLMGF0Sb507 zme|GOYHisfj#SO0D3K}1O~t)OEt1Jy+}20qM`J`E*C)|uPXkar2+bB8KS*zJJV^YW zz0Sii%kDFzG{G36%Z`Rx9e(AT`-swGEIthT`Bc0mREphPZ_pAd%jAVo*YzKr-4#?C zBQBvZEaD2bblA{dIMLwG(Cx5V3wbB5(N3p~h4cIT~F&+yC}dU736isUo*KemSEVUsrDGY`)PP)sxWkp!@c^bHV#?p%s!G zlrOHF$jp%)+Lw}G^!+h)6v}PMA^nMUn2J?y5f^Slc`wBFq(S`S3Ld9?D4^C^CpnoX zF6~*3*qO@C-LWNGwU6c&%kP>rXS8GDkE$m7S#VMh=gft_`#8S#_nze)B+Tc(SlyJg za&F~h&*%SvY&tTq>AL|@^Rm!H4BT;R@%k6TWa<>Pms4K;?k`vbb>ZN+WMSp^c-q<- z3eyuD^vR)|iNn1iRm~pGTbh{D)XH=?k16B1FrNFBQh(*Gp3rD|LX{H1dMcaQot~rb zHhN!jo|As}W&?MH@5CL3y3MR%msZfEv4Z8HF@7!ylh+Vju0?NNicq^xRA^<4xq$L} z@<6@K#W}0$ES&Kw7v_hM6w)Vze1YuHai~$fnP^(i75N?N6+HCjtP0zp;)gvl^tt|~ zM$a`e(LWwnAgAxXk&LDhV&|%#o3BnKUC>G z#vA+qpX-4xQHYD#0LZpa0d3lzqcfo!_h4%Fcr57(IFTAk7vDuPbl4Alq!F ze|)aB@?-S6p*i+0|BQ1rbAK#`*omxCs4K0nuJGK2EEBsHqs82rLln*6|2ZYdqb8}W z!vXTBq=z3m)b&ZJN!<+0X=mF);C5#kJF2t8%Nk-4>0PD3%~Xu@7hoc!6%DL5r4qTX z0}HSL+wrcfEoJ-V(ZILe8lW+j3G~BSqH#CneNp5bbzxKMe zXJ?v;1bpL=l(s@Vw>+WA2ql|~t2ot+vnqmT?vlJ1*5$HYOHf;t1rJ_Y zLJuOvx|}^8hSyrYU|3``IJI6U%4nPAdN{M1gjaIF8M2E~>eZGfK66tF_g1mK;Z8OH zxsPnus@1oXvvZGi-gLl`=AXu?7z@RrQf#^5zSdL>I;W7RwM_Ge8iL99FuNi|q%wt; z2=ye8Z4LSukWnbNQMC^t$=R@??${t3#PGyLN}n-~h;of*drPbOjN4@ws-S+rZ&2Kz zots=x2ozDTvvD<7qg`f|)T1t2iF5ki-jws%oJl!p&i>HeKV#&s&M_Z$gEBckZRcHF zdjFduux$SpN^EbN=$~K)>xkMdJc!Mt&zm5C4}*bOiEj)apaZCUn;@QtoDqc$#xg2R z9cjsib<=P1r(GT&9I?T!q4 za^)8Wvac*;rdS};z<1GwsmqXs54b|SIln-~h)%(H6IFj?K-dfCW_IExKvHZBBzj;T zx{7A$sHew#v879Ax)40C_$fRrpXnppaYG=&{`F*`a*#{(%_k5SHTyz$VmeG7l?K0~ zX+-i*>x_9Z_iu`%SpNdx65CN4vuyh&sW1&Ln}fbzL|Fw443X)it7eITSUbi{_15og z`3{a{fKmZY!Dk_h6HYKMsTsH;>Ladv#RRw)sFE{VJy$TP2;kUmx*Mgn3w z0xI((1H}KDIWqy+Rak+25}lNQN& zx%RTiW?<~8(9h?uD~kMg^`Wgp;fVxtPZ8y|$rv04PLCZ*HN38-ny9Y`Q+tf=#TVM3 zJFfZ{k|H-IY}pe7FO~-}oP9lV2kpclnSYfH5!dICd;i6)`~xM(YRhjiFs>LTMx>A`%NpvH_p~2Kt(wc_Z8a#Zv3|VtP_^IS z^C?E8;}n(85EvhoY{qYFstd1#BBJ5H2I!!sN#T?u`12RbzOEGWZDV~jzy6l{Z`9<2 z29T-W0Qqr~>mh!wf5C3mct{0JNsO$%863quV8vIQ;jMPx-;v$a5b^x!RG2jI4?Nz04BDK2bu#vFNjT_6{k3PGMD*Za(@vd|^!>J;zI*yny-ZO- zROhxRPl`jLnvZ3%>*H1P^;?Xpd4O2_0 zp952W7xqOLjGQOvK*uFrgP|4ac(5XO^E>7y>2q@|&WW7{9ptYuDzCn?G02I;!(gNB znNV%Kte$Y6&=E^Uh&XgIuE?p7tZF$lE=7!F^y*Q}Y;WC*N;h77Yd`YPw%#xxzR!Gn zwA7GAPGXDjhcq8284e!7LPCUWPi3?X)^@Xl356%|8ZrH*Lwggl{nA#!S&uarF8;$S zzulg;x3Co2`D}SCfwy6Ce}c1YSt?#0?|hL@jA&ru;kd$!vd#$;X|rlBpGoHK#r8+D z>O&w4;;I6N`dZ?_vbA4y_KyXJ^SaK_@F#0sV@#7AgbVLJ=050ETKFb5Nmd1?@^J6} z2YQ3ZS-CXZm2*?a8=gq$E?@EAkT^!j7`=GC+ z@~?t-DNTjFMZ+8;-fcUIE~B|CqRM9Aom+A5v}y=Sqr6i!_T7qwZ;>gkp6p9`6l-D6 zB7M$3a@R>Hvn06rBWGxj)ZlB;tTy%`ZcH10CO*`)U3i)Co81B?$pG)6i(V z;W-4Ivu>0EGw?gi)|Gwd7!yF<{3;A}jfy@r_uKyysQ_00!~hvHbPpa%CZURoLJDR| zy9~jQd1eJ=46Jd2tVjjMLVMt+DT z3-wBsEB;nM48SSVsO~9puGmc&2^V!M)olC Date: Wed, 3 Jan 2024 15:03:33 +0100 Subject: [PATCH 09/10] algorithms(bf-to-astar): factor out the heap and dirs Signed-off-by: Matej Focko --- .../files/algorithms/paths/bf-to-astar/bf.hpp | 4 ---- .../algorithms/paths/bf-to-astar/dijkstra.hpp | 22 ----------------- .../algorithms/paths/bf-to-astar/graph.hpp | 24 +++++++++++++++++++ 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/static/files/algorithms/paths/bf-to-astar/bf.hpp b/static/files/algorithms/paths/bf-to-astar/bf.hpp index dbad150..48bfb12 100644 --- a/static/files/algorithms/paths/bf-to-astar/bf.hpp +++ b/static/files/algorithms/paths/bf-to-astar/bf.hpp @@ -8,10 +8,6 @@ #include "graph.hpp" -const static std::vector DIRECTIONS = - std::vector{std::make_pair(0, 1), std::make_pair(0, -1), - std::make_pair(1, 0), std::make_pair(-1, 0)}; - static auto _check_vertex(const graph& g, std::vector>& distances, int v, bool check_only = false) -> bool { diff --git a/static/files/algorithms/paths/bf-to-astar/dijkstra.hpp b/static/files/algorithms/paths/bf-to-astar/dijkstra.hpp index 2b23120..3a1fb69 100644 --- a/static/files/algorithms/paths/bf-to-astar/dijkstra.hpp +++ b/static/files/algorithms/paths/bf-to-astar/dijkstra.hpp @@ -10,28 +10,6 @@ #include "graph.hpp" -namespace { -using pqueue_item_t = std::pair; -using pqueue_t = std::vector; - -auto pushq(pqueue_t& q, pqueue_item_t v) -> void { - q.push_back(v); - std::push_heap(q.begin(), q.end(), std::greater<>{}); -} - -auto popq(pqueue_t& q) -> std::optional { - if (q.empty()) { - return {}; - } - - std::pop_heap(q.begin(), q.end(), std::greater<>{}); - pqueue_item_t top = q.back(); - q.pop_back(); - - return std::make_optional(top); -} -} // namespace - auto dijkstra(const graph& g, const vertex_t& source) -> std::vector> { // make sure that ‹source› exists diff --git a/static/files/algorithms/paths/bf-to-astar/graph.hpp b/static/files/algorithms/paths/bf-to-astar/graph.hpp index 1bdebcd..2d59b87 100644 --- a/static/files/algorithms/paths/bf-to-astar/graph.hpp +++ b/static/files/algorithms/paths/bf-to-astar/graph.hpp @@ -81,4 +81,28 @@ std::ostream& operator<<(std::ostream& os, const graph& g) { return os; } +const static std::vector DIRECTIONS = + std::vector{std::make_pair(0, 1), std::make_pair(0, -1), + std::make_pair(1, 0), std::make_pair(-1, 0)}; + +using pqueue_item_t = std::pair; +using pqueue_t = std::vector; + +auto pushq(pqueue_t& q, pqueue_item_t v) -> void { + q.push_back(v); + std::push_heap(q.begin(), q.end(), std::greater<>{}); +} + +auto popq(pqueue_t& q) -> std::optional { + if (q.empty()) { + return {}; + } + + std::pop_heap(q.begin(), q.end(), std::greater<>{}); + pqueue_item_t top = q.back(); + q.pop_back(); + + return std::make_optional(top); +} + #endif /* _GRAPH_HPP */ From 2d7ba033694b20765faa516dcc16f15df4ddfdf5 Mon Sep 17 00:00:00 2001 From: Matej Focko Date: Wed, 3 Jan 2024 15:04:57 +0100 Subject: [PATCH 10/10] algorithms(bf-to-astar): add A* Signed-off-by: Matej Focko --- .../2024-01-01-bf-to-astar/03-astar.md | 224 ++++++++++++++++++ .../algorithms/paths/bf-to-astar/astar.hpp | 55 +++++ .../algorithms/paths/bf-to-astar/main.cpp | 6 + 3 files changed, 285 insertions(+) create mode 100644 algorithms/11-paths/2024-01-01-bf-to-astar/03-astar.md diff --git a/algorithms/11-paths/2024-01-01-bf-to-astar/03-astar.md b/algorithms/11-paths/2024-01-01-bf-to-astar/03-astar.md new file mode 100644 index 0000000..da3f8ef --- /dev/null +++ b/algorithms/11-paths/2024-01-01-bf-to-astar/03-astar.md @@ -0,0 +1,224 @@ +--- +id: astar +slug: /paths/bf-to-astar/astar +title: A* algorithm +description: | + Moving from Dijkstra's algorithm into the A* algorithm. +tags: +- cpp +- dynamic programming +- astar +last_update: + date: 2024-01-03 +--- + +## Intro + +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 + 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 + a non-terminating run of our brute-force method. At this moment we have made + some small arguments why are bounding is enough and doesn't prevent any + shortest path to _not be_ discovered. +3. Finally we have converted our bounded brute-force algorithm into the + Bellman-Ford algorithm. +4. We have mentioned the worst-case time complexity of our bounded naïve + approach and also the Bellman-Ford algorithm. Our worst-case depended on the + fact that we assumed the worst possible ordering of the relaxations. However + we could also try to relax in the most ideal ordering which could result in a + faster algorithm and that's how we got to the Dijkstra's algorithm. + +Now the question is, could we improve the Dijkstra's algorithm to get even +better results? And the answer is _maybe_! + +Dijkstra's algorithm chooses the next cheapest vertex for relaxing. This is good +as long as there is no additional information. However, imagine a roadmap of +some country. If you're in the middle of the map and you want to go south, it +doesn't make much sense for you to go to the north (in the opposite direction), +but a little bit might make sense, so that you can switch to highway and go much +faster. + +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 +least. + +## A* description + +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 +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. + +## Roadmap heuristic + +Let's have a look at the heuristic we could use for the roadmap example. There +are roads (the edges) and towns (the vertices). Cost could be an average time to +travel the road. What heuristic could we use to influence our algorithm to +choose a better ordering of the vertices when relaxing? + +In the former example we've said that it doesn't make much sense to go in the +opposite direction than our goal is… We could choose the distance from our goal +as the heuristic, e.g. right now we're 100 km away from our goal, using this +road makes us 50 km away and using the other road we will be 200 km away. + +## Heuristic for our map + +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: +$$ +\vert x_a - x_b \vert + \vert y_a - y_b \vert +$$ + +Since we cannot move in diagonals, it makes sense to maintain the distance in +the actual steps from the goal. + +## Passing the heuristic + +In our case, when we're using C++, we can just template the function that will +calculate the shortest path and pass the heuristic as a parameter. + +## Implementation + +Actual implementation is very easy once we have the Dijkstra's algorithm: +```cpp +auto astar(const graph& g, const vertex_t& source, const auto& h) + -> std::vector> { + // make sure that ‹source› exists + assert(g.has(source)); + + // initialize the distances + std::vector> distances( + g.height(), std::vector(g.width(), graph::unreachable())); + + // initialize the visited + std::vector> visited(g.height(), + std::vector(g.width(), false)); + + // ‹source› destination denotes the beginning where the cost is 0 + auto [sx, sy] = source; + distances[sy][sx] = 0; + + pqueue_t priority_queue{std::make_pair(0 + h(source), source)}; + std::optional item{}; + while ((item = popq(priority_queue))) { + auto [cost, u] = *item; + auto [x, y] = u; + + // we have already found the shortest path + if (visited[y][x]) { + continue; + } + visited[y][x] = true; + + for (const auto& [dx, dy] : DIRECTIONS) { + auto v = std::make_pair(x + dx, y + dy); + auto cost = g.cost(u, v); + + // if we can move to the cell and it's better, relax¹ it and update queue + if (cost != graph::unreachable() && + distances[y][x] + cost < distances[y + dy][x + dx]) { + distances[y + dy][x + dx] = distances[y][x] + cost; + pushq(priority_queue, + std::make_pair(distances[y + dy][x + dx] + h(v), v)); + } + } + } + + return distances; +} +``` + +## Running on our map + +For this algorithm I will also show the example of a call: +```cpp +distances = astar(g, std::make_pair(1, 9), [](const auto& u) { + auto [x, y] = u; + return std::abs(1 - x) + std::abs(7 - y); +}); +std::cout << "[A*] Cost: " << distances[7][1] << "\n"; +``` + +First argument to the function is the graph itself. Second argument is the +source vertex where we start. And finally the lambda returns +_Manhattan distance_ to the goal. + +And we get the following result: +``` +Normal cost: 1 +Vortex cost: 5 +Graph: +############# +#..#..*.*.**# +##***.....**# +#..########.# +#...###...#.# +#..#...##.#.# +#..#.*.#..#.# +#D...#....#.# +########*.*.# +#S..........# +############# +[Finite BF] Cost: 22 +[Bellman-Ford] Cost: 22 +[Dijkstra] Cost: 22 +[A*] Cost: 22 +``` + +## Comparison + +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 +distance to our goal. And now if we run our code, we get the following output: +``` +Normal cost: 1 +Vortex cost: 5 +Graph: +############# +#..#..*.*.**# +##***.....**# +#..########.# +#...###...#.# +#..#...##.#.# +#..#.*.#..#.# +#D...#....#.# +########*.*.# +#S..........# +############# +Relaxing path to goal in 40. relaxation +Relaxing path to goal in 68. relaxation +Relaxing path to goal in 89. relaxation +[Finite BF] Cost: 22 +Relaxing path to goal in 40. relaxation +Relaxing path to goal in 68. relaxation +Relaxing path to goal in 89. relaxation +[Bellman-Ford] Cost: 22 +Relaxing path to goal in 41. iteration +[Dijkstra] Cost: 22 +Relaxing path to goal in 31. iteration +[A*] Cost: 22 +``` + +From the output we can easily deduce that for both brute-force and Bellman-Ford, +which are in our case identical, we actually relax three times and for the last +time in the 89th iteration. + +Dijkstra's algorithm manages to find the shortest path to our goal already in +the 41st iteration. + +And finally after introducing some heuristic, we could find the shortest path +in the 31st iteration. + +:::danger + +Please keep in mind that choosing bad heuristic can actually lead to worse +results than using no heuristic at all. + +::: + +## Summary + +And there we have it. We have made our way from the brute-force algorithm all +the way to more optimal ones. Hopefully we could notice how the small +improvements of the already existing algorithms made them much better. diff --git a/static/files/algorithms/paths/bf-to-astar/astar.hpp b/static/files/algorithms/paths/bf-to-astar/astar.hpp index 210e348..bc144af 100644 --- a/static/files/algorithms/paths/bf-to-astar/astar.hpp +++ b/static/files/algorithms/paths/bf-to-astar/astar.hpp @@ -1,4 +1,59 @@ #ifndef _ASTAR_HPP #define _ASTAR_HPP +#include +#include +#include +#include +#include +#include + +#include "graph.hpp" + +auto astar(const graph& g, const vertex_t& source, const auto& h) + -> std::vector> { + // make sure that ‹source› exists + assert(g.has(source)); + + // initialize the distances + std::vector> distances( + g.height(), std::vector(g.width(), graph::unreachable())); + + // initialize the visited + std::vector> visited(g.height(), + std::vector(g.width(), false)); + + // ‹source› destination denotes the beginning where the cost is 0 + auto [sx, sy] = source; + distances[sy][sx] = 0; + + pqueue_t priority_queue{std::make_pair(0 + h(source), source)}; + std::optional item{}; + while ((item = popq(priority_queue))) { + auto [cost, u] = *item; + auto [x, y] = u; + + // we have already found the shortest path + if (visited[y][x]) { + continue; + } + visited[y][x] = true; + + for (const auto& [dx, dy] : DIRECTIONS) { + auto v = std::make_pair(x + dx, y + dy); + auto cost = g.cost(u, v); + + // if we can move to the cell and it's better, relax¹ it and update queue + if (cost != graph::unreachable() && + distances[y][x] + cost < distances[y + dy][x + dx]) { + distances[y + dy][x + dx] = distances[y][x] + cost; + pushq(priority_queue, + std::make_pair(distances[y + dy][x + dx] + h(v), v)); + } + } + } + + return distances; +} + #endif /* _ASTAR_HPP */ diff --git a/static/files/algorithms/paths/bf-to-astar/main.cpp b/static/files/algorithms/paths/bf-to-astar/main.cpp index aca946f..6e70904 100644 --- a/static/files/algorithms/paths/bf-to-astar/main.cpp +++ b/static/files/algorithms/paths/bf-to-astar/main.cpp @@ -40,5 +40,11 @@ auto main() -> int { distances = dijkstra(g, std::make_pair(1, 9)); std::cout << "[Dijkstra] Cost: " << distances[7][1] << "\n"; + distances = astar(g, std::make_pair(1, 9), [](const auto& u) { + auto [x, y] = u; + return std::abs(1 - x) + std::abs(7 - y); + }); + std::cout << "[A*] Cost: " << distances[7][1] << "\n"; + return 0; }