From 19f2b7a7fa745435428a2bc2427133464664ea0c Mon Sep 17 00:00:00 2001 From: Anthony <bouillant.anthony@gmail.com> Date: Thu, 3 Apr 2025 15:43:26 +0200 Subject: [PATCH] Create game file --- Makefile | 16 ++++-- puissance4.c | 88 ++++++++++++++++++++++------- puissance4.h | 29 ++++++++++ src/actual_output.txt | 15 ----- src/game.c | 77 ++++++++++--------------- src/game.h | 11 +++- src/test_board.c | 42 -------------- src/test_game.c | 128 ++++++++++++++++++++++++++++++++++++++++++ tests_board | Bin 0 -> 26728 bytes 9 files changed, 274 insertions(+), 132 deletions(-) create mode 100644 puissance4.h delete mode 100644 src/actual_output.txt create mode 100644 src/test_game.c create mode 100755 tests_board diff --git a/Makefile b/Makefile index 6d85b74..bc8b3ac 100644 --- a/Makefile +++ b/Makefile @@ -3,11 +3,12 @@ CFLAGS = -g -Wall -Wextra -Wpedantic LDLIBS = -lm LDFLAGS = -fsanitize=address -fsanitize=leak -puissance4: puissance4.c src/board.o src/game.o +puissance4: puissance4.o src/board.o src/game.o $(CC) $(CFLAGS) $^ -o $@ $(LDLIBS) $(LDFLAGS) board.o: src/board.h game.o: src/game.h +puissance4.o: puissance4.h clean: @echo "this rule must clean everything up (including candidate files in testbed)" @@ -29,8 +30,13 @@ tests_rand_ai: puissance4 tests_smart_ai: puissance4 $(MAKE) -C testbed/smart_ai -generate_board_test: src/test_board.o src/board.o - $(CC) $(CFLAGS) $^ -o src/test_board $(LDLIBS) $(LDFLAGS) +tests_game: src/test_game.o src/game.o src/board.o + $(CC) $(CFLAGS) $^ -o tests_game $(LDLIBS) $(LDFLAGS) + ./tests_game -tests_board: generate_board_test - ./src/test_board \ No newline at end of file +tests_board: src/test_board.o src/board.o + $(CC) $(CFLAGS) $^ -o tests_board $(LDLIBS) $(LDFLAGS) + ./tests_board + +run_tests: tests_game tests_board + @echo "All tests executed successfully." \ No newline at end of file diff --git a/puissance4.c b/puissance4.c index 85b0e6b..f216479 100644 --- a/puissance4.c +++ b/puissance4.c @@ -1,19 +1,16 @@ -#include <stdio.h> -#include <stdlib.h> -#include <stdbool.h> -#include "src/game.h" -#include "src/board.h" - -#define AI_SEED 0 -#define MIN_COLUMN 4 -#define MIN_ROW 4 - -void flush_input(); -bool is_valid_game_mode(int game_mode); -bool is_valid_board_size(int rows, int cols); -bool is_column_full(int col, int rows, int** board); -int select_random_column(int number_of_columns, int number_of_rows, int** board); -int select_smart_column(int number_of_columns, int number_of_rows, int** board); +#include "puissance4.h" + +// #define AI_SEED 0 +// #define MIN_COLUMN 4 +// #define MIN_ROW 4 + +// void flush_input(); +// bool is_valid_game_mode(int game_mode); +// bool is_valid_board_size(int rows, int cols); +// bool is_column_full(int col, int rows, int** board); +// int select_random_column(int number_of_columns, int number_of_rows, int** board); +// int select_smart_column(int number_of_columns, int number_of_rows, int** board); +// void announce_winner(enum GAME_MODE game_mode, int current_turn); int main(int argc, char const* argv[]) { if (argc != 4 || !is_valid_game_mode(atoi(argv[1])) || !is_valid_board_size(atoi(argv[2]), atoi(argv[3]))) { @@ -26,10 +23,63 @@ int main(int argc, char const* argv[]) { } enum GAME_MODE game_mode = atoi(argv[1]); - int rows = atoi(argv[2]); - int cols = atoi(argv[3]); + int row_count = atoi(argv[2]); + int col_count = atoi(argv[3]); srand(AI_SEED); - return play_game(game_mode, rows, cols); + + int** board = create_board(row_count, col_count); + if (board == NULL) { + fprintf(stderr, "Error: Unable to allocate memory for the board.\n"); + return EXIT_FAILURE; + } + + fill_board(row_count, col_count, board); + printf("Board size is %dx%d (rows x col)", row_count, col_count); + print_board(row_count, col_count, board); + + int current_turn = 0; + struct coordinate empty_slot; + + while (true) { + int selected_column = get_selected_column(game_mode, current_turn, col_count, row_count, board); + if (!is_valid_column(selected_column, col_count)) { + continue; + } + + empty_slot = find_empty_slot(selected_column - 1, row_count, board); + place_token(current_turn % 2, empty_slot, board); + print_board(row_count, col_count, board); + + if (does_player_win(empty_slot, row_count, col_count, board)) { + announce_winner(game_mode, current_turn); + break; + } + + if (is_board_full(current_turn, row_count, col_count)) { + printf("\nIt is a draw.\n"); + break; + } + + current_turn++; + } + + if (destroy_board(row_count, board) == EXIT_FAILURE) { + fprintf(stderr, "Error: Unable to free memory for the board.\n"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +void announce_winner(enum GAME_MODE game_mode, int current_turn) { + const char* winner = (current_turn % 2 == 0) ? "Player one" : "Computer"; + + if (game_mode == PLAYER_VS_PLAYER) { + winner = (current_turn % 2 == 0) ? "one" : "two"; + printf("\nPlayer %s won!\n", winner); + } else { + printf("\n%s won!\n", winner); + } } bool is_valid_game_mode(int game_mode) { diff --git a/puissance4.h b/puissance4.h new file mode 100644 index 0000000..ba2d9cc --- /dev/null +++ b/puissance4.h @@ -0,0 +1,29 @@ +#ifndef _PUSSIANCE4_h_ +#define _PUSSIANCE4_h_ + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include "src/game.h" +#include "src/board.h" + +#define AI_SEED 0 +#define MIN_COLUMN 4 +#define MIN_ROW 4 + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include "src/game.h" +#include "src/board.h" + +void announce_winner(enum GAME_MODE game_mode, int current_turn); +bool is_valid_game_mode(int game_mode); +bool is_valid_board_size(int rows, int cols); +int select_random_column(int number_of_columns, int number_of_rows, int** board); +int select_smart_column(int number_of_columns, int number_of_rows, int** board); +bool is_column_full(int col, int rows, int** board); +void flush_input(); + +#endif diff --git a/src/actual_output.txt b/src/actual_output.txt deleted file mode 100644 index 8885cb0..0000000 --- a/src/actual_output.txt +++ /dev/null @@ -1,15 +0,0 @@ - -┌─┬─┬─┬─┬─┬─┬─┐ -│ │ │ │ │ │ │ │ -├─┼─┼─┼─┼─┼─┼─┤ -│ │ │ │ │ │ │ │ -├─┼─┼─┼─┼─┼─┼─┤ -│ │ │ │ │ │ │ │ -├─┼─┼─┼─┼─┼─┼─┤ -│ │ │ │ │ │ │ │ -├─┼─┼─┼─┼─┼─┼─┤ -│ │ │ │ │ │ │ │ -├─┼─┼─┼─┼─┼─┼─┤ -│ │ │ │ │ │ │ │ -└─┴─┴─┴─┴─┴─┴─┘ - 1 2 3 4 5 6 7 \ No newline at end of file diff --git a/src/game.c b/src/game.c index b80875a..530c90f 100644 --- a/src/game.c +++ b/src/game.c @@ -23,60 +23,41 @@ struct coordinate find_empty_slot(int x, int rows, int** board) { return coordinate; } -int play_game(enum GAME_MODE game_mode, int rows, int cols) { - int** board = create_board(rows, cols); - if (board == NULL) { - fprintf(stderr, "Error: Unable to allocate memory for the board.\n"); - return EXIT_FAILURE; - } - - fill_board(rows, cols, board); - printf("Board size is %dx%d (rows x col)", rows, cols); - print_board(rows, cols, board); - - int player_selected_column, player = 0; - struct coordinate coordinate; +bool is_valid_column(int column, int col_count) { + return column >= 1 && column <= col_count; +} - while (true) { - if (game_mode == PLAYER_VS_PLAYER || player % 2 == 0) { - printf("\nColumn number? (starts at 1):"); - scanf("%d", &player_selected_column); - } else { - if (game_mode == PLAYER_VS_RANDOM_AI) { - player_selected_column = select_random_column(cols, rows, board); - } else if (game_mode == PLAYER_VS_SMART_AI) { - player_selected_column = select_smart_column(cols, rows, board); - } - } +int get_player_input() { + int column; + printf("\nColumn number? (starts at 1):"); + scanf("%d", &column); + return column; +} - if (player_selected_column < 1 || player_selected_column > cols) { - continue; - } +bool is_board_full(int current_turn, int row_count, int col_count) { + return current_turn + 1 == row_count * col_count; +} - coordinate = find_empty_slot(player_selected_column - 1, rows, board); - place_token(player % 2, coordinate, board); +int get_selected_column(enum GAME_MODE game_mode, int current_turn, int col_count, int row_count, int** board) { + if (is_human_turn(game_mode, current_turn)) { + return get_player_input(); + } + return get_ai_move(game_mode, col_count, row_count, board); +} - print_board(rows, cols, board); - if (does_player_win(coordinate, rows, cols, board)) { - if (game_mode == PLAYER_VS_PLAYER) { - printf("\nPlayer %s won!\n", player % 2 == 0 ? "one" : "two"); - } else { - printf("\n%s won!\n", player % 2 == 0 ? "Player one" : "Computer"); - } - break; - } - if (player + 1 == rows * cols) { - printf("\nIt is a draw.\n"); - break; - } +bool is_human_turn(enum GAME_MODE game_mode, int current_turn) { + return game_mode == PLAYER_VS_PLAYER || current_turn % 2 == 0; +} - player++; - } - if (destroy_board(rows, board) == EXIT_FAILURE) { - fprintf(stderr, "Error: Unable to free memory for the board.\n"); - return EXIT_FAILURE; +int get_ai_move(enum GAME_MODE game_mode, int col_count, int row_count, int** board) { + switch (game_mode) { + case PLAYER_VS_RANDOM_AI: + return select_random_column(col_count, row_count, board); + case PLAYER_VS_SMART_AI: + return select_smart_column(col_count, row_count, board); + default: + return -1; } - return EXIT_SUCCESS; } bool does_player_win(struct coordinate coordinate, int rows, int cols, int** board) { diff --git a/src/game.h b/src/game.h index f9dc934..75d431c 100644 --- a/src/game.h +++ b/src/game.h @@ -1,5 +1,5 @@ -#ifndef GAME_H -#define GAME_H +#ifndef _GAME_H +#define _GAME_H #include <stdbool.h> #include "board.h" @@ -15,9 +15,14 @@ struct coordinate { int y; }; -int play_game(enum GAME_MODE game_mode, int rows, int cols); struct coordinate find_empty_slot(int x, int rows, int** board); bool does_player_win(struct coordinate coordinate, int rows, int cols, int** board); void place_token(int player, struct coordinate coordinate, int** board); +bool is_valid_column(int column, int col_count); +bool is_board_full(int current_turn, int row_count, int col_count); +int get_selected_column(enum GAME_MODE game_mode, int current_turn, int col_count, int row_count, int** board); +bool is_human_turn(enum GAME_MODE game_mode, int current_turn); +int get_ai_move(enum GAME_MODE game_mode, int col_count, int row_count, int** board); +int get_player_input(); #endif diff --git a/src/test_board.c b/src/test_board.c index 050c064..018c493 100644 --- a/src/test_board.c +++ b/src/test_board.c @@ -4,23 +4,18 @@ #include <string.h> #include "../src/board.h" -#define EXPECTED_OUTPUT "src/expected_output.txt" -#define ACTUAL_OUTPUT "src/actual_output.txt" - #define GREEN "\033[1;32m" #define RED "\033[1;31m" #define RESET "\033[0m" void test_destroy_board(); void test_fill_board(); -// void test_print_board(); void test_create_board(); int main() { test_create_board(); test_fill_board(); test_destroy_board(); - // test_print_board(); return EXIT_SUCCESS; } @@ -38,43 +33,6 @@ void test_destroy_board() { printf(GREEN "test_destroy_board: PASSED\n" RESET); } -// void test_print_board() { -// int rows = 6; -// int cols = 7; -// int** board = create_board(rows, cols); -// assert(board != NULL && "Board creation failed"); - -// fill_board(rows, cols, board); - -// FILE* expected_output_file = fopen(EXPECTED_OUTPUT, "w"); -// assert(expected_output_file != NULL && "Failed to open expected output file"); -// freopen(EXPECTED_OUTPUT, "w", stdout); -// print_board(rows, cols, board); -// fclose(expected_output_file); - -// freopen("/dev/tty", "w", stdout); - -// FILE* actual_output_file = fopen(EXPECTED_OUTPUT, "r"); -// assert(actual_output_file != NULL && "Failed to open actual output file"); - -// char actual_output[1024] = { 0 }; -// fread(actual_output, sizeof(char), 1024, actual_output_file); -// fclose(actual_output_file); - -// FILE* expected_output_file_read = fopen(EXPECTED_OUTPUT, "r"); -// assert(expected_output_file_read != NULL && "Failed to open expected output file for reading"); - -// char expected_output[1024] = { 0 }; -// fread(expected_output, sizeof(char), 1024, expected_output_file_read); -// fclose(expected_output_file_read); - -// assert(strncmp(actual_output, expected_output, strlen(expected_output)) == 0 -// && "Output does not match expected output"); - -// printf(GREEN "test_print_board: PASSED\n" RESET); -// destroy_board(rows, board); -// } - void test_create_board() { int rows = 6; int cols = 7; diff --git a/src/test_game.c b/src/test_game.c new file mode 100644 index 0000000..3cd6b99 --- /dev/null +++ b/src/test_game.c @@ -0,0 +1,128 @@ +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include "game.h" + +int** setup_board(int rows, int cols, int default_value) { + int** board = create_board(rows, cols); + if (board == NULL) { + fprintf(stderr, "Error: Unable to allocate memory for the board.\n"); + exit(EXIT_FAILURE); + } + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + board[i][j] = default_value; + } + } + + return board; +} + +void test_place_token() { + int rows = 6, cols = 7; + int** board = setup_board(rows, cols, EMPTY); + + // Player1 + struct coordinate coord = { 2, 3 }; + place_token(0, coord, board); + assert(board[2][3] == PLAYER1); + + // Player2 + coord.x = 4; + coord.y = 5; + place_token(1, coord, board); + assert(board[4][5] == PLAYER2); + + destroy_board(rows, board); +} + +void test_find_empty_slot() { + int rows = 6, cols = 7; + int** board = setup_board(rows, cols, EMPTY); + + // Search for an empty slot in a column + board[5][2] = PLAYER1; + struct coordinate coord = find_empty_slot(2, rows, board); + assert(coord.x == 4 && coord.y == 2); + + // Search for an empty slot in a column with multiple tokens + board[4][2] = PLAYER2; + coord = find_empty_slot(2, rows, board); + assert(coord.x == 3 && coord.y == 2); + + destroy_board(rows, board); +} + +void test_is_valid_column() { + int cols = 7; + + assert(is_valid_column(1, cols) == true); + assert(is_valid_column(7, cols) == true); + + assert(is_valid_column(0, cols) == false); + assert(is_valid_column(8, cols) == false); +} + +void test_is_board_full() { + int rows = 6, cols = 7; + + assert(is_board_full(41, rows, cols) == true); // Full board + assert(is_board_full(40, rows, cols) == false); // Not full board +} + +void test_does_player_win() { + int rows = 6, cols = 7; + int** board = setup_board(rows, cols, EMPTY); + + // Test horizontal win + board[5][0] = PLAYER1; + board[5][1] = PLAYER1; + board[5][2] = PLAYER1; + board[5][3] = PLAYER1; + struct coordinate coord = { 5, 3 }; + assert(does_player_win(coord, rows, cols, board) == true); + + // Test vertical win + fill_board(rows, cols, board); + board[5][2] = PLAYER2; + board[4][2] = PLAYER2; + board[3][2] = PLAYER2; + board[2][2] = PLAYER2; + coord.x = 2; + coord.y = 2; + assert(does_player_win(coord, rows, cols, board) == true); + + // Test diagonal win (left to right) + fill_board(rows, cols, board); + board[5][0] = PLAYER1; + board[4][1] = PLAYER1; + board[3][2] = PLAYER1; + board[2][3] = PLAYER1; + coord.x = 2; + coord.y = 3; + assert(does_player_win(coord, rows, cols, board) == true); + + // Test diagonal win (right to left) + fill_board(rows, cols, board); + board[5][3] = PLAYER2; + board[4][2] = PLAYER2; + board[3][1] = PLAYER2; + board[2][0] = PLAYER2; + coord.x = 2; + coord.y = 0; + assert(does_player_win(coord, rows, cols, board) == true); + + destroy_board(rows, board); +} + +int main() { + test_place_token(); + test_find_empty_slot(); + test_is_valid_column(); + test_is_board_full(); + test_does_player_win(); + + printf("All tests passed successfully.\n"); + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tests_board b/tests_board new file mode 100755 index 0000000000000000000000000000000000000000..74e2f07dd6600edda11e746f20c1c567898064ad GIT binary patch literal 26728 zcmb<-^>JfjWMqH=W(GS35YIsnBH{p{7+%Cd84L^z4h$9yybKNuY7D9jYzzzxEMPH+ zJWM@|zQF_$htV7mE(0@Ep9F}(z`%e`%Rtq^XpoygLLeGsABc?&dq6FK(F_6*RUmz= zASRR#6Nk~JoFGXC1{e(!2k8UbrvQ;>K%*tVk_-$m8d)DGY%Fvk`YdcwX;^stfQAoD zABYRm7XZ~40M!Sh4L}MQ7#LtQ)Vts~06E118lLF10mOO+7>%wk1F8?5c7f`H(I7iO zLcvc<Qb25UdtiK+Jun((UjS6!7e$aM3=HUW2}GCyMuY4C2?ahaNdbimh)oQJMRO3; zK3wr|0vet$8Wi6k^YwEwlgv!?b5eA3GV@9+bSo^(bj?ikiu3i1K+@pw1|<bhnsWCG zWnf}pH~?}hG#D9}8X$a_c_29v1_p4t2dNKpjWXTCzDCyfnuYs|68#C*K8KHj)PUR# z(gRWhvIt}z)QKQ2$Q}kh1_p2*1&0S?BS@SHB*xFcz;H43T(0G(ZdkSf$)VsN1_lNp z6cGr=4Tm_$Eg*kFR3XUqIMg@b5MPc%JPU_7C>*fatHZzm&w>coFfb(GFeeg+cpnb& zR2<@k3=9lP$QDA`3m6y}1R10m6rcqjES)pN$EWA!=fxM7Bo>v#$1}uxhWN&(q!y*7 zXBL;F7KQja=j7+5h9oBCq(bDd%fu%ZC+5Xx=4F;Jq?H$CmZUNi6lLa>q%ouwrKU0z zl$I1T<R<3i<R>%4$0t`L#;0ZGCFW#Sr830FCl(i{7L~-OC1&O@#K-4kCM821lbe{C z$5337l3G**G9fu5v4|nHBC~`cCo>6T9Vn5TfjGHvRx*U;?&Im?9B-s&Y{CFz8tIvW zQzIh-BLfo{f>;pD3>5<f6-f3p0|Ts>W7slr8XG7#T!o62N@a2~FfcJZ04WC5C(v>a z7M=>wMC*Yj{s3A}aiE1ij1Mb!LFpJdAKkErSOm&LpgacQF)%zp5{KChl6!$94y`Ic z!VDjf#6fuwEXcsX@B>Ml2POzgyHIz6$_-F{g-OkTs%Hm>CQOupVF8jjsC<CQtUwY6 znFSNyfFus`8%%r$k~qi?nD_xCaW0r30|UbeBynye@e4@eyh!4s%+U}S4S~@R7!85Z z5Eu=Cp%Vh1`Q?6jG{52S=w>~s&%of(dZ2{q{{@fcBOHgpp7?K?q|d<c>Az~SJ_7^4 zJOfC521w@9%ZLB}|DOQngBn+#US5RrK@F`>FAu``pa#~bmmA@HP($X^%Y|@0r~&io z<wQ6i)NuLqvJuV)HCR5qEQIkt{Z};wyT*k9?4D2mRnO{yT!-KvMdR;9<8MXduSMf8 zMdQy!<4;BA8@~1Iycp-v`N*UB$%lXtSHlB_Cp~&?Pcbks{1=_9$H4F<O^;u`1r%xw z9=*0R^%xjDdRZrdD9axuq8{C>VIX$vffCOD5BTLlT_}dbP}-x{HWMV-YnlY2H2-;Y ze(*T>&feqT4`vU>V;&d(m56wBv&w=bTMv|Q{C|+fFYm$t*AI%H23uP_1_p*w?NE<q z+h3r81&_|BB|IM8wrqL~3@=vx`~Uw%%K!iWkFkpCF)%R39_E)v(f_ji|Ns9WgF-zz zpY8y8<HchJ1_lUg8zYPbiY2gE>|vN<kLEW5$6Y^w;`6xc7m$MP&@bJtA3Cpf9)Hot z%D~{!>H5Q?x%LM`sjf#iYrif?<lrtf1_p+GphV@-%bLv#5<C7v_}~Bk`$1waB>(;Y z@6jFlquUp3)Va>%FPxbe7{EfVA3VTj&t_s^XgyG(;nB<5#RoFr;4W1LaIiap%{%7N z%jyYc9e;82@BjZEy{uUfF4zYzSpWV1Kj9>@KNN8K;}{Fb0+>HQR+>Yt1a-DOdRfo& zfE0kN)PvaZBJJ=0|Hob5fP(V1N4M)6j~O1lB1b`7k8al=9*h?}x*0rL50ruodj-~W za91eA{Y`Mg1i=!=Ur7G_|G(Mw4P&?K8*A4OH5)v7S=As?5WiT0{09mzSm4w#GB6P9 zxtGi!w<CM53T*blU7#MfM=$FqZjb_y=K{elJN}~c4@UefW?*1A?)n1c!Pi+ZkB4@< zzQE#f0Z4cpe8A+vc%d5>i!4xE(;%Tx1GhC1YOC-cq)_-$v$)yy1!K4C3s8vkdGxY| zK$Jm3L>=ZrScv2>poB<g?VHZh7apCV501P306W_0xa%J<Ll+c3$g%YR99sh2u75mc zct9L|9Hf$=+4T=&x9cBk*B3RXn_Yh}cDw$tc70Q`ZNdbU{GrfU`^KZQ^a0EQWr#_L z<N&dN!K3r|i}>FlKSGUtQ*+g$+w}oR-2M0e|6re?q*#N_+B==4H#|B+FL?B_UIvHQ zaj>!%YQI704-y~d9=)u)ktI2Q|Nr0ZdZYOTqowPMS`m+4)&<DYuYQ3X0S(+6H3vO< zSz93TFYf;W84FI<FWDKv*)10$1+jKLSpEYjcwR?Bq8Z`O3$TFv01wD+hR)+Jf|(c? z`1c*?JpRJr7s6p5zz*X_Ha!sRun&ygt{<#jZ`929=w*G+1xlR9U$}vF-T`~*O>MSE zFY8T+48&dHV0V21y9*Q?Ymg;>{Y2RM1#Ig=Wa-;KLBa8bvD@_v$W~Brv_j-xTmVZ! zGv*!sp0}WatCuwkA`P*6&QDN~K@u6rYC~kndL*lVfUPz{mQDbh4^3H+m=J@=zX$~z z`v+8BzLxEF{nH7`pno~Rp#ia28LSzUmTp+P{;9n^0jWd?_Go^i0ZBdGu6MxE!3;`g z=YRbF@6qY{!lSwN1w$zp*r1nd!Fd}zZu4>?0|P_1>kY6DrtZ)O9?6$HdU-%&JfPzF zg-5T9Ajq6{usO`2)Qg;zUVu{oxZra??)n5A9m?IVPf!X8n4-?(F9Lpmg8m6(x9byY z*9YM2@B(DK#Sdt9crDWG`hc<9^?|kP9sZu=|DbZo^@T@w><>gn>8$<JT>IyL32$@l zAI4I?9T5x+46k{1_=70c9iSn|SWrs{RHK30N${2t$a$l9Gz3ONU^E0qLtr!nhGqzW z=4FswE*))XZEToZl3H96pORW!Qj}jApOl|ilwzge=IH6;>cS-*ZIH`QT$HR2Rs)vP zOJ+b(t5K9+UaX@4meW+QwN-G9@C=C$4s~{R4GxCb2DZltt33gZ!ND+l7{F#IDB3Fc zh5Gm?sHrI^Ie}cGkX)3SSdy8arvRGYN=<=J&4JZNXU3vv3Cb^50MC+PYC(1f*vwRz zcafa|Gc`IZ7VI70fDq8cA_D^hvi`KpoE&VrAwGtX;6OtOEQmOa1j{2k1SSEc!SV=; z7`Pry>0x*@rGeqmlr;>Grc7XXG^L5*(Udt1kEZNlcr;}R!=ouv7#>a8!tiLy3<d?& z6p%5D(#+Nc3=9mQ`5T>g|Nk#xU|`tu?*IQY3=9ls-u?d%YI*s*|Np;-fq}vF!~g%F zo=?(;|Nmte85p!a{{L^o$iTq<>Hq&2Mg|7S&;S3|FfuSSeg6M{4kH7@ozMUO?*WN_ z`~UwABLf5L_y7Mvlh&ZQZ?N4}K@5x)0*ul;>>Lvq85ls~pvmeT@BaS>b^94y*g;dj z3@Qu^3?>W=3=MDp|DOQT$S2^&C*j4<UCz<KU@v8*Wvl{@bCCWR1_p)~Z~y-XYXqrf zfS3zfL-643|Nr0t6{tAKE|9+r-~ay)8VO-=<r8RQa^_`A;b8~O_krZsFfcIKz5oCJ z1V|4|o@qZ=0Z1McPS4)||9=-n{t8?k)B#%j;s1XjQ1ct6pP7>pqyVI!hmnC{>c{{8 zGg0&>fTS5f@;ZzR4EH|%{~v`S9|)KCVPs&C`1Jq3Eoht#ZoUZ<NCBw9n#0JzVEX0% ze~>%i{&Q_+X1WJb!2q@c6!w3={QnP{Plu~XhRB1g9mS&|Fd71*Aut*OqaiRF0;3@? z8Uolu0Jbg-whj%{<O8YP0wR#savenDgVw@<gy3t#pp92hn;ayrAqgT8YaL<h*r3a1 z89;4SkP4_W1_$W6IBAd|0|Q)M3d)DAn*+5kK|(P7U;q8j2k|$6=4crh7(i38Abtdt z0h+o4@ozxoVQa*|ZA%6Q2H4s#36LTN1_n_m4Wppi7#g7K#z0M0klX<f0d1#)n4q~q z5G@WO7#J8jpaM{eVFuJ8Fn5C|5f~U4U~BPU_RB%_!_@u$5Ah!(G)VqI`5932{zLh& zc>4h5yFlfCLisR%K#ivw1zXpRZlD1)Jvcz=04SXRr3;{R1C*Wsr58YHbn^)DVe2K` zot>=|G(wX~^GZq;3=Q>6^bB+jOTk>jIwK=J0~1ZCFt{55iB4Exqgn#$S}-w8V1<S} zgC=wd=M#t~RK*Mo3=s7zxM1pep=k-ev>r`I4=X6SGJ-7^{>I9{0L~l6P*ozuEDQ{c z&D=Rm3=E<lSs54@yTof)7#P^K7#JA%ij9pJW{DJl_|_nPsj(5mG%=7`c5g7>%!FaO z%nXoxGy?+zQy>ci1Ctt$87OBlxq?L0!5jxBaYhCPCQS&3AGDg8NgK>z5a3wB#K6F$ z3*j*^IB?{G`1-;iLqH3-#rA;s#@wJOO{k*<#6hzuOwo+o<)AeZECO>G7#MCafpiEQ zW@TWw31&)s1-a%vGdF0_c0GR|3j+hQm{1tV3`WU2%nS?y%$gjLObiTytxOCITKpeC zW(h{KFfeF?ea9hHl9<lGtOMroaPqS<FzA8uGuTefovaKD`nJpr3?MEerzaBwgFz~k z%fz{bg@M7)ixDE5!UeL;n0Y@71H&{%1_sao05`~S3}6Wp=1K+zhV_gL44{d7ummf} zEv%p*hA1&*e#^+faEXzD;W;A%1DX=3I?&|4C5Jo%1H&&+E@fh1;Qj^jk<b-J1_o>X z3Cs)(Nh}Nul1vN?tRM!1v;i{%1G6(fD1m@tTL>iOBIdx#zyRVpffRxm3^MycEAn0W z8Cf7QtYA?Fc2Q8A9s(IM4;)(p3~VgyQp^ktETEx}d5m2ksrihcvWADn2CVu#D+2?| zByhC-VyXmjCWASDnMxQL7+9u&IUkvxgAADp=6qt>0g{;p=6q%{V_{%mnGWWBWfEs+ zU|^X6=6qw42AMV!%=ymro{52hWfqw8hp8LHnGNRrV7da@Q8Ne3`N=dFWZGOX=O5Ee zkg|DT&KjnEkfrm%oZU<hK*|<?Ij5M^Kr#!#oYPEaK@MF4=A7XG6~Zjbz(KLOYpWwi zD9FI&U_PJO16KwXXGR8wg^Y7S!LkS(EDS6!Ss56XFoLQT=A}>}Py$`fco?K=1ytx6 zBLl-K#%my<)leaIW(J0}j2}Qk>%c<H2f!2q%W_5rh7F9&pya+0DrCyez_6K73?#G# zDrCXRz_5)`6C|`9DkQ|jz_63i1|+l#Dpb$Hz_5qWA0)IFDzuJ~f#DD%C<QSeh6u4S zG8r*3Ft9PQU14HikY!=b%qwAF2bCIZ9PFaZ3=C`>tP9ye3OG)K)G@Iquz&>F1(+Ea z7_&jue+7tW1rd`#1ZcYe;|355Q~@%A;*1frmWJ^iNbDyA0|O7M1Sl2quu6go9v)UH zP-bBS&8G8$iUD3wYU2fEZpJt!1_n7^7EsaF!^FVAI1@xH0}-1+!~qa-7DU_v5zj%y z7ZAY+avUFskOmPNAi@+xIDrU%5D^0+GC@QoGXsM>8^2&N69a=nC<6lnn>ZIMD+2>3 zGrUD&4$4pRY78RK@|Yh~@*HDlV34(CWMJ@PWWXr-!$HMpF*^eT(?xMBP!@!QCNm_H z32`tpg2R&;5}wSAY#?tiGqJjJGB5~3Vu2YF@*FPg3=GUOBy&L~HghsCFwc|*u~|Sf z(aex|;{>rq)HxX#Zm<|~u!Dl2n1g}Am>pd3GRCtrFqm<I#soPSLpT^1%)x8{#$HYa z1`9A-g7G;g1A`@)t-yGQgMq;c%+_G!<78m42D1$qJJ}f+Y`|;_#wrd523s)Of$;(d z1A_yY?ZLR8lYzkz%no3j&B?&v1ZGDt8gMc&xQHn*F)$=B=CCs`xQVDTF)(B>?qg?Q z@Bq8Bfbj_j1A`}+UBWnzgMq<|8#KOI!Pv&Zz~Czs2pTJ8jOS!v@Z$$fVK*^Wu`@9E zb7(O!Ff=pX<6vM2<hNpCVCZ06#KFK2$qO12oxnJigMlGh+=YpOVFqIeCj&!_q#qLl z!ve<hoD2-HU^lE_+{(eg5Xb4k0LptSI2jn?xj>7_b}+u-U|>i9yXOGodQJw0Bo5F@ zjUx;k+MEmwtn8AYs!LRclYxOnn~!T12Ll6(G^p4B3A2G>1mqfaQ0y}>un03TFmO!* zsj&yK0ztY!6%SZpG7|#>SfMZj3n=!)c5pH<uubFxi8FJs&H<$W4se!ZnZdxoAkGA` zp9K`T94u^WK&wZlfeJAGPaF&kj1s>%7#QjqByB<2HieM~r0fW&9&Tb{V31;vw&Y}B zV0Yzx3KL=ii8DEZYJ3ojK_HNwfq|D5r0qXQ4T!-6Vll9=fQn2IU#5bSfq{dYO_iB} zfe9o4A;79+^Eeq8I189~K^x`3;tT?591ILx>>%kN6derQ#h^I(Eet9+85qFB$)I9l z&I@J+2G$mK28LKpu<;WUSnqOzVhO@aVa?@WV2I}inaDITgJ%^x1H%tyP@dfYZXz)- z@MeL=%^4UNsv$-*@MSWj@IiD(@D@WQL41%rUlv2E5QxJx5j51!1`1`6dEiLlXW#@m zhdYsjfx%A>>QwN^IM}JBEKsNVqc}AH%7ZvH5XGsjEFh;&fH}1aY9@5T1?1Eas8eGY z7{HDJ=>R)Mkbx6qA-4}Z1A}xJw7m%)#s@p*3CuAvQ0pP#CJW_393zL~m@i<*6hIvV zu?{2#woZtF6J(^^BTfbeMqzNGfHiW#a|>Wg8P<bhi-D2jGBX1MH@I9(VS<#4=q=n0 zj0_AspmLD~rCj7iDe_=R9<_{w6rHF=B_9X01mp)rGczQQ2tcw2C`Gffux$pFo8auh z0nQ$RknF*!%EiFIB*Vl9YQTZkv4R$gGB9w0at(6|BM(UTBT!`kB7_*&n-~}vgcU$~ zw3&E+fh0g08ALd^7#J9%*+AC+g!a%tt$a|TVw|JR$iTq(jgx^PS|79<k7;5K0~aVO zc(QL{WMJThss}YpA?iV*jG$ESDFL#dX`&A!DAju^L3kdbpj6N3#SK!&G%<q34Qj$l zkakc8&Sdc8gQyK)nE{mq%`t)GnX(uH#UPR)3=9nHJ3(_?AimfiP~?iTU4ZsRz>_ZE zh`kD`NkHxqg=&PjM^pi%jcK9>Bgj3XIuPeLu>OJ?04hmAHiAlKrsVt_1_`L6A{bZ$ z#XvF)4Dz6cE&~I@Bv27klwZ!E2vrlqS_oAF>S}-#7lM?4j8PSW*qgw>!U^p~!>j^Z zV+eI+3MfOODcA-x#1?8#1_R4&s2Wgd53-0UlfeZlo5R4s1t|wXR+d3M2A-}0hm0Yp z<p2s9UU85Drin4&u;5jOghc>LCsYeO^fMXw5N3;m%1=gCHdyum&y#@-21h@uD9lh$ z^s~xCcoASLShZnR$V07w&NnkKWiqfqt;k@}fJ)kdJO)Y{Sq$vZ-~%lMWneU6a2IA^ zU@~D~=Ck6q=C<Va6t<C+;bvf9wqmeSWVB|rR1}mDW?*2kWMt-p3UD(DGcd5SGI$Cz zFtDjGaC6&nGxISpu&aSZIMB>uV_@I}3n?;Nb6YBk<neh5+e<<$;nHKYmjsy$lH$(e zvyn^@1{ux`GTcOxfq{pE0qiVA5s)Kz`5CwsIY8F(i7_y9fSdz%F#`iX*an0LSQ!`u zz(R^lATRKMeWfVCz#zyA@+sIU+@8V~+zbptXl?*01-U_3kWrOSn44K%T3$e$fk6Zu z0AL$HvZ7)jbHH4Xd&Iz&LAf@PGVBZt;!q)0Xqd7xFi4;Y@iH(-3NteEadU$`2@Y2& zUXahACV|{1t;YZkBd`!C@N6U*7-XQ1MiG>S#svohgB&Q{<*gur#|;WQ1*qNN0JE25 zl3-v^L^c^@Dgz@VbXXt>0c1N9C?*&fn4w1VF)*;eBY_ocq#`3kHONFZMn+F?1hOkK zcna%tGca(Vh7%_wjc^GwF!O<I<c9hKY=|P8AfphwgG7xaD1>=Hrh5vTfSt<A%?OfX zVBj+W1rJy`x2G`3PJRsrW<F4g6@Zz=0dkPKBrGBY5fKS80~DD;{0!XO3=G0hUvV%n zh@i&<QUo(FphPby@iH)ovoJFA*>Q7odkP~WPXg=~P_#nb!fnlJ$&GM|6sRzkhRbn- z<k}e}ZNMHziBol`pe$T5*dVxKIpnzW6b5+=6mX!zgB6rAkwXZSyAhcM6lTcL2@6?P z2m=)KY>-H2hsQJr*k9ZX44kOWfdw%<AA{2Z$iFZLfsEtEi~t_gWWkGw2tG*E@Iw-c z0JcyT1iOZpfk6lqoWiJ1f`p|ATo4?_umGw82ec?CWkDhhl7z%iRU^Ay9Gg@XXfPU7 z-m)@qFo3(QjEukeco`TO!3T_hs(VI8d1lZET|EN>!$MHO%P7yp&kkD2!FV3TeF-8! z>rI%1LCTdu1Xwl{w3dW{g8@|SF=8{89n{g3XMzkwfySF2fd-VA7#Ji#bsNYUE=Q1Q zU_u!rro+U*z-$8QlURcY@UV~<s4olZ$FN0%Sgbq@oJ_DL9kUQ4D{J%o`K&w(SQ#T( zr43j)cbBpXm$Gtj=(BQ`GARqQvM%J%XJBGsWaIE+W6}_2W#q7Bm10)6fvIGLN|&(m zu`y|gu(Fo0azZt%VO3yYVqs!q(hy`72btl;%G$<Z%gV?h%qn4rVE|Y=ABO?R2qAq| z&JtE$h(S!6LaZuqlcXUgfsJm2o7({~mx-BykrC7l1I3pYD34$ZSQc2oazg``2^5%Y z8KBk=m;i+o6DahUK*7ok3Oz<vBL+?;Sp6-=%EzIIaHtRi6AQCGt0;)#NMn@*(YwHI zW=>_r>R`rpRxKY^J`R15A;KW_`mCHFio*sRYRs$QUTbCL*$s*vV*^%Jjwn_>4qK2O zPJLDh4qH~9G*;e7Ry9!Qa9F~uV(nn%dk$6<#VW*M2MS7aeO4QeG*+oyth^IgW#+RA zq_GM{vT8x~$wR`Fvy)ZgIn+oo4qH~<5>`QdRs{}QR*^JT(MVQ3sB&e9a^4Pz#mrex z;5IW$?;_TPPr)t(`B@(lR2=Z=WNc;C@L?6^&}Ze~@MaY#W#wA{QY;e5$~S>kgd>7g zsL7U<ha-(uEE42MPzW>YvkHRD;;>;=1<|Wnp`PMxWfj>C4pS!P$`V+p@-TDlVrBGZ zHOpXSUCS!4r@Wk%F`9{qfsrvjKBqV_k0IN+w5TXGuOzr6F*)0<C^0wHH!(dknIS&P z&)d*2KE60HFS8`GDz(Va0IVdWC^0$JGyrszg=-l@c6feKN^oXXs%M^4W=S#Vw2J)X z#2m=k7X?MB;Nvf{eG@AJ5=%0CQuESFGC(HdG}zh1HNZPJiy_-LKczG$H8j-I1!Ot{ z$cW(5+}y;XN~kIBsU?1?6(yc|#U(|h$)NRw0m%&6j>U<2?m78Mi8(>3DOLG-2+!cO z($Fx((LFxY&)d&G+|QLE+c_~gBh@D}FBM`j$V5lbF&&9PnduoN(7*wEkRd)k1r&tw zQGU2hHvpOL92y+r@9PS2A2=9&QqxLMoCtQhXI@EBqJL6WYBEfcAwIqYY)5=<YHmSM zej1V)U<aWX5}uip6Iz_=mRMX8npd8gm%<Pqp9|KG$BED|&tiy=uP92aWN=R{2`EY} z%giq=Mh)Gh{Cv=PB%p&vAm^HZQvn0WgD$R)K0f}=4B0`cC7DI3DPTV$rKW7xqN4mF z-_+vb#Pn3B(zLYHB5?4QfKn5ZmqLqE9n(rui-ME$3!rfaiu#a@qSQo)p<r#<t`#Mz zMR|!i0nSdDB@Cr`#hK}OsVNHJ15v;s=~z^hSm~UfpPdOa1{9Ib`FSOo>81Il#UNH< zW?pKMzrPbCPIL0}(iOl2+&IvfaeRD6ImpMLV1Z_p0C4WW8ReFs009SwE8esYNqEqZ zBcRkABW7Kb7$Dgd5+ESo1!p9t<d?fA7BGN=ADmN>!qzu41tqcIOSVP^*z%WaE|PKJ zcnV4_DJ{x_1R%UDU?>J1V^f@wUsR%i7MAeRgdsjYGp{7IC^@wNly@1j5l;3^tbkbK z2~v=knB$R}SOBpC6ta-Kgg0cJO+ZBxC`ceV7nIo`24n}N=A<STr-tPF2ZN0-OU&_0 zElVu|r($R!0I@x^IJGDsKeISLFEzzCH8;Pg5@JMrd}$J)EM;VZD-*$j5acJfqEu+& z#1|EYM((L4@Ra9T26ibpc|l6W)O5eZ+*F90L8gK{iyF`IDQO@Fmll*1F}VAMDmZIv z8=63;f)#Xgi!+K#vK4f5OG@)nZPQcpQj0Q^6?AhGi;^>JA!lbP=%y>^`Wq?e7MG;h zrstJ{_3NeucseWSrsd`9CY5I9lw{^XIH?uTI4y?oi%Lo|6?D_`b2CeH(?Io$Zb3dc z>J@a;O2LJcZVBitC6My6%;L<X%$&@UO52Rgl$2Dck(mXFx|zvo3c6{@X}SeP`6a2~ z0^c^RG$%(vH!ZQaGA}u!C_gX1v{)D2$YM~v2Gf*ZqzhUXpO>znn+B0c&PgoJfJtZO z=Rv~J6IADcPGbVEI`=P1NiBj!esOYXN<Qo;r}+5%w6ypV2Jq5u)TQC^p8oNmLMt8= z>hZ}L;FFs&t5V}j7~;WMBdIiv0kqhj0i-giG%Y?Uu{f2%6}&zjTnvE;ND&ke<QftZ z8SfVA=N#hc?*}@RDlIc7H7_3$pN8g$*d!2(R1GZj2myNq?CI3J6iA?;tcQ<J%SlWx z2CD*j7^EmZxwI%gIX|Z~HxDeHoS%}KTvoympP!QwpP!aioLT}F&rK}KPAy`H2OXYO zlv*4Q3O!K3m4i;!f`qZBe|&LbSt=wP<8$(pvl$>38ZpG@=BK1G1o${cx&|3SX(I+F zf5#w~_}~!75Ld7r5VOFp^7IEA0E#S6|9Ce~A6JHWh!a3<9OU!4z;USnjwVn4`0~t@ z)cBOdl0=61GH|Mf`2l=N7dV+9?EnBLA2SoEE#QCyCjyYiz+M5nCB7gr#grkwG_SNc zHN^;a{1-?~Qetv;X#v#3u#m~gEG}V4&d&iIFO~@jREWpGMi-YP!cHQC>~~-&DN4*M zPRlRK1)VWgk{Vx<4@#{?kYmcACzR>w>BA9d|42?Is0f6#3-v*@BbbPXHu&_C3kn$Y zL4`t!etL4UuA!lBL~4j-P9%&6S)2k@QDI>gZ)TzkIjb){uM~8AAH13Xo67*VTTf3P ztM&L*z)g!!&d<%w&tpIcfD59$#GLq))U;x~3?vzdQWPO@i2-i0=w&c~d=06>@)C1U z_-TpBsmL-Q7lUIy3AFu1FPT9<BR@A)KQXT)DZf-d7u?V-)-U!9c8)hRGKw!K%1<v! z%*_QCJ@Lh<g{7%^C7G!?IjM&FB?bBgrJ2RWiFwJXCJg$e#YLdd0tKr+Mlc#0>SyL9 z=ai<TLKH$dm<p3X4R^3vc$MgvR2HNbqnHKWO9Y7%P_j%(P1DO@04oE98@OE#&O2aU zN@hMdm_SF{>19B62SFrqGLt}zqGWwo2tmhou~VP{b{2+@|IuYZ>&X}ySQvVs3%)@6 zDL`sKlTg^ygVumBF$gdeK+mBAsRdz>K9KkVG;z@22S^;YUJ#@fggrssZw6@w4e0rB zAoqd9K~wIG4B`y1^?M*O5Uv3YlrRV|z}6pv#6Y+MY>pUwJs?O7ghA&oVsk%e4>vY( z&|wse3_=XBnJ<w4LFU{Bn=ixwo1TNtu`zsvio<3YVaw3=f>xzq31^Ub*z5%@bYNr< zVGsdr00LQvgt4gyEr(!W5Qd*43RA8D8tY+@gs(e<iQ9q2B^c1x-N}O1H!w(n4!4CV z1=EU5pz|^X7+~k&g7+vfFfh0?g2cf}Pzcaq2`o&ICFVfQfsKj4<}{)h5o<h=)o0=` zzY2%=6p%QRAOmdOD9o%`AaNK6i3Tlp5n>QvK%dsyg+u*8Mg|5!CQ$|p=sHvIsyhY- zhHE(7{|TfXbkZm&BY@KY#BxS(I78UTBxr4b0G9CAV8VXxCTLNZAd?h>05reA%<_kZ zGx|()B4|+xlN1ANog++r2vj}#bYmV=9DUldjR|yyr8I`UpqV-W25ANZH1|x0nu9)x zzZ@!#K1mH)uK<b{^hvc{AopWAXB4zD23vZ&1a>E8_<+`eVN?GA>>gOCAn3n1#F>~u z>0bc8v<Eid9Rf-}Ogx}KLKp&K1t%Ak80zJN4kk^?j0Y`1OfD)ZE-6h*1MeP%9e)~M zk{h24IvN$!L`unzPX~{~#HW<x7Zt}RmR2w%=jRsWq?V+n=ox^9PB2w~&OptKPXu+{ z<5TlWiYghv9ftUn(%jrihWL0S9(Vu(MJc#KMs9(i)VcBT7;R`hBL-}uhFC>GE_MrY z^mUDQ^>YEWzvJEgLgQUM;vs?_E<p_O?mqrbjz01JZf?P@A@L!OPCl;j4Dl|JevZDL z&agvZL8rSWLeGUoJ7^ZvMsz?vofV`OR9%8kf<=`{%goD!9RZ6X13qLn9$b+k&X>)> z9F)U<s;z%~aY;&MUOcGDiT99OP+J$&a4af8JH{5pQStF1zRu8)14j#Lw;(>HI6pok zF)t-26=!oaK0c|q7-kcwT?{q{sWA=aqxQidC*`8J1k_4qh))GM8_|hFJ7E{D!Po>8 z#L$K`gI;lEZb@PigI;k-5rocwvB1s90tUUj{E}3?^t@8Nf};F_)S{9~Bq4BnST{2T zD&*+osSC<VVC5N!#Tg8GDV2G}mAMeQq=*45Q<hp(4C<C5bK;8_^omk*5<wcEtb&{p z20id#t6o7)iC%hs2?Ll>oSMv_SCU#$!k`CEF?vP$V6W??W<YaOMoJL_oX4OC%|P+s zOb9U&#s(PxW2aQ+CFW)(Gw4A~ODZmA&;zxYQ}ap~^ioojO4H*LixTtFQ;T8T%)GRG z7&|d3sVKD!#>>gfONFtELH>s52MNc6_@E+)5)9f`0cw-M)~@3^uO4OxNG*&FqLo35 zO2BP45RZWYwEPRig3+*Z(m`S%469#3Y!C*m{RFLIgV~RMPCI&Y3A8{Oqz{Hc?Mr0+ zu=XvChE~%s<>2$p;T#4A2GEktZ~y=2!|Vqw>;=igXblhtwD1Q?qq{#5x>p3IAGV(a zMmInM4CF=-6Q*ARY96S)1ZtAQ^uzY2z-ZVqA*gcj`RFh<hz6bC4chAg(+}I<0i$95 zhi>~|fT@Ggbqov)puHn7K5YL8jE2P@y8A(PgK#gj4FuB<+s^`{Vf$HN_M`iMI#j<C z$S|b+F|d6ypyk9MDUg29G%UJ)&}1tpTtV_6b?}3@p$&CV`UZ<b3H0<c2i(4AV1VgY zfa-(M;63_Ky&wuI4Yvl=|3J2X15^QwhH^n#!5GGe(cm@HNa^bUR02kW*0X{&LJ6oe z-2R<t?uWJKVf0f_#6k6fD42c_8-zhS1(4kjYiGk~*uE{0S`bDLKS+NHbiz4E7IY9e zD*X{;I8+Ekq1%5J>V8=I!F6u}NG}MZ+YjltA?(ip&8jmnFu-Wg(nF9w7>1=E82>5s zoOVzi1c|}=0kC@&(AC5EFdB5Y8t8m<n10xPH3w+J5vC4gHVDJ`FdCGhLFc8z^uzkY z9z^N~ow)!?yD<H*{vYg~4cL7euw($U7Zwkzq2UkH58Kbypbv5_0|R>cgXxFSpnfVS ziedU0VEqJW!2z=eVj64<6pY8fAPwEX2Gb9_Z$JQtepq=1GeH5|?`44B{{a(+>DPt{ zKxvR$K};B}3Dpl0L&nxn`_cUm(+5*)fkXdpsQzeB;(#fJhBXqG!3BqYG3dTRYv_JO zm>zWdgK+2%gkEg&4i>Ob1JL!yLG?qO3suFyU=LkmsRP>>36+8AhtZ(^Jcy01KO1yL zC<6mS0W4$V&=1m!rUb0=0cf=<s6K%DAJh&2E!>2qJ6Qb*8rMSeB3OS0bk!z1nk2|R ZumUIn8pc9X0+z{-1yc-_Xd2MC3;^qZ25JBR literal 0 HcmV?d00001 -- GitLab