From a8b50a2e94877a9541cb584746b25201b9f6fe20 Mon Sep 17 00:00:00 2001 From: Julia <145168563+julia-aph@users.noreply.github.com> Date: Tue, 26 Mar 2024 15:11:58 -0500 Subject: [PATCH] bluh mew --- .vscode/settings.json | 3 +- source/io/ctrl.c | 162 ++++++++---- source/io/ctrl.h | 20 +- source/io/input.c | 60 +---- source/io/input.h | 6 + source/io/platforms/win.c | 4 +- source/io/platforms/win.h | 3 +- source/io/platforms/winhandler.c | 27 +- source/io/platforms/winhandler.h | 2 +- source/main.c | 36 +-- source/test.cpp | 422 ------------------------------- test.exe | Bin 79535 -> 80047 bytes 12 files changed, 172 insertions(+), 573 deletions(-) delete mode 100644 source/test.cpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 646f1fa..f3124c4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -50,6 +50,7 @@ "stdexcept": "cpp", "streambuf": "cpp", "typeinfo": "cpp", - "execution": "cpp" + "execution": "cpp", + "stdbool.h": "c" } } \ No newline at end of file diff --git a/source/io/ctrl.c b/source/io/ctrl.c index 83776c8..45dcc35 100644 --- a/source/io/ctrl.c +++ b/source/io/ctrl.c @@ -24,8 +24,10 @@ struct InputRecord { union { struct { - u32 value; bool is_down; + } key; + struct { + u64 value; } axis; struct { u32 x; @@ -36,16 +38,31 @@ struct InputRecord { double timestamp; }; -struct InputResult { +struct InputBuffer { + struct InputRecord records[IO_BUF_SIZE]; size_t count; - struct InputRecord buf[IO_BUF_SIZE]; + pthread_mutex_t mutex; }; +struct InputBuffer NewInputBuffer() +{ + struct InputBuffer buf; + buf.count = 0; + buf.mutex = PTHREAD_MUTEX_INITIALIZER; + return buf; +} + struct Axis { + u8 type; + union { struct { u32 value; - bool is_down; + u32 is_down : 1; + u32 is_up : 1; + } key; + struct { + u64 value; } axis; struct { u32 x; @@ -104,87 +121,126 @@ hashtype hash_ident(u16f id, enum InputType type) return Hash(&obj, sizeof(struct ident)); } -struct ctrl_bkt { - hashtype bind_hash; - size_t index; - hashtype code_hash; - +struct code_bkt { + hashtype hash; + u16 code; struct Axis axis; }; +struct bind_bkt { + hashtype hash; + u16 bind; + struct Axis *axis; +}; + struct Ctrl { - size_t capacity; - size_t filled; - struct ctrl_bkt *bkts; + struct { + size_t capacity; + size_t filled; + struct code_bkt *bkts; + } codes; + + struct { + size_t capacity; + size_t filled; + struct bind_bkt *bkts; + } binds; pthread_mutex_t mutex; }; typedef struct Ctrl Ctrl; -Ctrl NewCtrl(struct ctrl_bkt *bkts_prealloc, size_t capacity) +Ctrl NewCtrl(struct code_bkt *codes, size_t c, struct bind_bkt *binds, size_t b) { + memset(codes, 0, sizeof(struct code_bkt) * c); + memset(binds, 0, sizeof(struct bind_bkt) * b); + Ctrl ctrl; - ctrl.capacity = capacity; - ctrl.filled = 0; - memset(bkts_prealloc, 0, sizeof(struct ctrl_bkt) * capacity); - ctrl.bkts = bkts_prealloc; + ctrl.codes.bkts = codes; + ctrl.binds.bkts = binds; ctrl.mutex = PTHREAD_MUTEX_INITIALIZER; + return ctrl; } -struct ctrl_bkt *get_bkt(Ctrl *ctrl, size_t i) +void CtrlPoll(struct InputBuffer *buf) { - return &ctrl->bkts[i]; + for (size_t i = 0; i < buf->count; i++) { + struct InputRecord *rec = &buf->records[i]; + + switch (rec->type) { + case KEY: + key_update(); + break; + case AXIS: + axis_update(); + break; + case JOYSTICK: + joystick_update(); + break; + } + } } -struct ctrl_bkt *find_bind(Ctrl *ctrl, hashtype bind_hash, hashtype search) +struct ctrl_bkt *get_code_bkt(Ctrl *ctrl, size_t i) { - size_t i = bind_hash % ctrl->capacity; + assert(i < ctrl->codes.capacity); + return &ctrl->codes.bkts[i]; +} - struct ctrl_bkt *next; - for (size_t offset = 0; offset < ctrl->capacity; offset++) { - i = (i + 1) % ctrl->capacity; +struct ctrl_bkt *get_bind_bkt(Ctrl *ctrl, size_t i) +{ + assert(i < ctrl->binds.capacity); + return &ctrl->binds.bkts[i]; +} - next = get_bkt(ctrl, i); - if (next->bind_hash != search) - continue; +struct code_bkt *find_code(Ctrl *ctrl, hashtype hash) +{ + const size_t index = hash % ctrl->codes.capacity; + size_t i = index; + + while (i != index - 1) { + struct code_bkt *bkt = get_code_bkt(ctrl, i); + if (bkt->hash == 0) + return bkt; - return next; + i = (i + 1) % ctrl->codes.capacity; } return nullptr; } -struct ctrl_bkt *find_code(Ctrl *ctrl, size_t *i, hashtype search) +struct ctrl_bkt *find_bind(Ctrl *ctrl, hashtype hash) { - struct ctrl_bkt *next; - for (size_t offset = 0; offset < ctrl->capacity; offset++) { - *i = (*i + 1) % ctrl->capacity; + const size_t index = hash % ctrl->binds.capacity; + size_t i = index; - next = get_bkt(ctrl, *i); - if (next->code_hash != search) - continue; - - return next; + while (i != index - 1) { + struct bind_bkt *bkt = get_bind_bkt(ctrl, i); + if (bkt->hash == 0) + return bkt; + + i = (i + 1) % ctrl->binds.capacity; } return nullptr; } bool CtrlMap(Ctrl *ctrl, u16f bind, u16f code, enum InputType type) { - if (ctrl->filled == ctrl->capacity) + if (ctrl->binds.filled == ctrl->binds.capacity) return false; - hashtype bind_hash = hash_ident(bind, type); - struct ctrl_bkt *bind_bkt = find_bind(ctrl, bind_hash, 0); - bind_bkt->bind_hash = bind_hash; - hashtype code_hash = hash_ident(code, type); - size_t code_i = code_hash % ctrl->capacity; - struct ctrl_bkt *code_bkt = find_code(ctrl, &code_i, 0); - code_bkt->code_hash = code_hash; - - bind_bkt->index = code_i; - ctrl->filled += 1; + struct code_bkt *code_bkt = find_code(ctrl, code_hash); + code_bkt->hash = code_hash; + code_bkt->code = code; + + hashtype bind_hash = hash_ident(bind, type); + struct bind_bkt *bind_bkt = find_bind(ctrl, bind_hash); + bind_bkt->hash = bind_hash; + bind_bkt->bind = bind; + bind_bkt->axis = &code_bkt->axis; + + ctrl->binds.filled += 1; return true; } @@ -199,11 +255,9 @@ struct Axis *find_bind_axis(Ctrl *ctrl, u16f bind, enum InputType type) return &get_bkt(ctrl, bind_bkt->index)->axis; } -struct Axis *CtrlGet(Ctrl *ctrl, u16f code, enum InputType type) +struct Axis *CtrlGet(Ctrl *ctrl, u16f code, u8f type) { - hashtype code_hash = hash_ident(code, type); - size_t code_i = code_hash % ctrl->capacity; - struct ctrl_bkt *code_bkt = find_code(ctrl, &code_i, code_hash); += struct ctrl_bkt *code_bkt = find_code(ctrl, code, type); if (code_bkt == nullptr) return nullptr; @@ -216,12 +270,12 @@ bool CtrlUpdateKey(Ctrl *ctrl, struct InputRecord *record) if (axis == nullptr) return false; - if (record->data.axis.is_down) { + if (record->data.key.is_down) { axis->last_pressed = record->timestamp; } else { axis->last_released = record->timestamp; } - axis->data.axis.is_down = record->data.axis.is_down; + axis->data.axis.value = record->data.key.is_down; return true; } diff --git a/source/io/ctrl.h b/source/io/ctrl.h index f6a8425..d13c3c5 100644 --- a/source/io/ctrl.h +++ b/source/io/ctrl.h @@ -25,8 +25,10 @@ struct InputRecord { union { struct { - u32 value; bool is_down; + } key; + struct { + u64 value; } axis; struct { u32 x; @@ -37,16 +39,23 @@ struct InputRecord { double timestamp; }; -struct InputResult { +struct InputBuffer { + struct InputRecord records[IO_BUF_SIZE]; size_t count; - struct InputRecord buf[IO_BUF_SIZE]; + pthread_mutex_t mutex; }; +struct InputBuffer NewInputBuffer(); + struct Axis { union { struct { u32 value; - bool is_down; + u32 is_down : 1; + u32 is_up : 1; + } key; + struct { + u64 value; } axis; struct { u32 x; @@ -83,8 +92,11 @@ typedef u32 hashtype; struct ctrl_bkt { hashtype bind_hash; + u16 bind; size_t index; + hashtype code_hash; + u16 code; struct Axis axis; }; diff --git a/source/io/input.c b/source/io/input.c index 1d56aad..f017283 100644 --- a/source/io/input.c +++ b/source/io/input.c @@ -7,67 +7,35 @@ #include "ctrl.h" #include "fumotris.h" -#include "gametime.h" #ifdef _WIN32 #include "win.h" #endif -bool dispatch(Ctrl *ctrl, struct InputRecord *record) +#define IO_BUF_SIZE 8 + +struct input_args { + Ctrl *ctrl; + struct InputBuffer *in_buf; +}; + +void *block_input(void *args_ptr) { - switch (record->type) { - case KEY: - return CtrlUpdateKey(ctrl, record); - case AXIS: - return CtrlUpdateAxis(ctrl, record); - case JOYSTICK: - return CtrlUpdateJoystick(ctrl, record); - case WINDOW: - return CtrlUpdateWindow(ctrl, record); - case ESCAPE: - default: - return false; - } -} - -bool write_result(Ctrl *ctrl, struct InputResult *result) -{ - double now = GetTime(); - pthread_mutex_lock(&ctrl->mutex); - - for (size_t i = 0; i < result->count; i++) { - if (result->buf[i].type == ESCAPE) - return false; - result->buf[i].timestamp = now; - - dispatch(ctrl, &result->buf[i]); - } - - pthread_mutex_unlock(&ctrl->mutex); - return true; -} - -void *block_input(void *args) -{ - Ctrl *ctrl = args; - struct InputResult result; + struct input_args *args = args_ptr; + Ctrl *ctrl = args->ctrl; + struct InputBuffer *in_buf = args->in_buf; input_loop: bool success; + #ifdef _WIN32 - success = WindowsBlockInput(&result); + success = WindowsBlockInput(&in_buf); #endif - if (!success) { + if (!success) exit(1); - } - - if (!write_result(ctrl, &result)) { - return nullptr; - } goto input_loop; - return nullptr; } diff --git a/source/io/input.h b/source/io/input.h index 1688a2c..1a75c3b 100644 --- a/source/io/input.h +++ b/source/io/input.h @@ -8,4 +8,10 @@ #include "ctrl.h" #include "fumotris.h" +struct InputBuffer { + struct InputRecord records[IO_BUF_SIZE]; + size_t count; + pthread_mutex_t mutex; +}; + void StartInput(Ctrl *ctrl); \ No newline at end of file diff --git a/source/io/platforms/win.c b/source/io/platforms/win.c index efb36cd..f006dd6 100644 --- a/source/io/platforms/win.c +++ b/source/io/platforms/win.c @@ -21,9 +21,9 @@ bool WindowsInit() return true; } -bool WindowsBlockInput(struct InputResult *result) +bool WindowsBlockInput(struct InputBuffer *buf) { - return WinBlockInput(result); + return WinBlockInput(buf); } bool WindowsWait(double seconds) diff --git a/source/io/platforms/win.h b/source/io/platforms/win.h index 7a89e9a..b539fdd 100644 --- a/source/io/platforms/win.h +++ b/source/io/platforms/win.h @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include @@ -9,6 +10,6 @@ bool WindowsInit(); -bool WindowsBlockInput(struct InputResult *result); +bool WindowsBlockInput(struct InputBuffer *buf); bool WindowsWait(double seconds); \ No newline at end of file diff --git a/source/io/platforms/winhandler.c b/source/io/platforms/winhandler.c index f805b1c..343c9a6 100644 --- a/source/io/platforms/winhandler.c +++ b/source/io/platforms/winhandler.c @@ -7,6 +7,7 @@ #include #include "fumotris.h" +#include "gametime.h" #include "input.h" struct Windows { @@ -45,7 +46,7 @@ void set_key_record(struct InputRecord *record, KEY_EVENT_RECORD win_key) { record->type = KEY; record->id = win_key.wVirtualKeyCode; - record->data.axis.is_down = win_key.bKeyDown; + record->data.key.is_down = win_key.bKeyDown; if (win_key.wVirtualKeyCode == VK_ESCAPE) record->type = ESCAPE; @@ -100,27 +101,31 @@ bool dispatch_record(struct InputRecord *record, INPUT_RECORD win_record) return true; } -bool WinBlockInput(struct InputResult *result) +bool WinBlockInput(struct InputBuffer *buf) { - INPUT_RECORD buf[8]; + size_t win_size = IO_BUF_SIZE - buf->count; + INPUT_RECORD win_buf[win_size]; DWORD count; - if (!ReadConsoleInput(windows.input_handle, buf, 8, &count)) + if (!ReadConsoleInput(windows.input_handle, win_buf, win_size, &count)) return false; + + double now = GetTime(); + pthread_mutex_lock(&buf->mutex); - size_t unused_offset = 0; for (size_t i = 0; i < count; i++) { struct InputRecord record; + record.timestamp = now; - bool include = dispatch_record(&record, buf[i]); - if (record.type == ESCAPE) - return false; + bool include = dispatch_record(&record, win_buf[i]); if (!include) - unused_offset += 1; + continue; - result->buf[i - unused_offset] = record; + buf->records[buf->count] = record; + buf->count += 1; } - result->count = count - unused_offset; + + pthread_mutex_unlock(&buf->mutex); return true; } diff --git a/source/io/platforms/winhandler.h b/source/io/platforms/winhandler.h index 25797d0..7bb4172 100644 --- a/source/io/platforms/winhandler.h +++ b/source/io/platforms/winhandler.h @@ -13,6 +13,6 @@ bool WinInitTimer(); bool WinInitConsole(); -bool WinBlockInput(struct InputResult *result); +bool WinBlockInput(struct InputBuffer *buf); bool WinWait(double seconds); \ No newline at end of file diff --git a/source/main.c b/source/main.c index 4edf4da..e519d90 100644 --- a/source/main.c +++ b/source/main.c @@ -73,39 +73,13 @@ void Loop(Ctrl *ctrl) falling.blks = falling_blks; for (int i = 0; i < 7779997; i++) { + + TetrMapToTermBuf(&board, &term); TetrMapToTermBuf(&falling, &term); size_t size = TermBufToChars(&term, out, out_max); - //puts(out); - puts("\x1b[H"); - - //falling.y += 1; - - for(int j = 0; j < ctrl->capacity; j++) { - printf("val:%u, is_down:%u, x:%u, y:%u, press:%f, relse:%f\n", - ctrl->bkts[j].axis.data.axis.value, ctrl->bkts[j].axis.data.axis.is_down, - ctrl->bkts[j].axis.data.joystick.x, ctrl->bkts[j].axis.data.joystick.y, - ctrl->bkts[j].axis.last_pressed, ctrl->bkts[j].axis.last_released); - } - /* - struct Axis *axis = CtrlGet(ctrl, LEFT, KEY); - struct Axis *right = CtrlGet(ctrl, RIGHT, KEY); - - printf("ctrl:%u\n", axis); - printf("ctrl:%u\n", right); - - printf("%f\n", axis->last_pressed); - - if (axis->data.axis.is_down) { - printf("left"); - falling.x -= 1; - } - - if (right->data.axis.is_down) { - printf("right"); - falling.x += 1; - }*/ + puts(out); WindowsWait(0.1); } @@ -115,8 +89,8 @@ int main() { WindowsInit(); - struct ctrl_bkt bkts[16]; - Ctrl ctrl = NewCtrl(bkts, 16); + struct ctrl_bkt bkts[13]; + Ctrl ctrl = NewCtrl(bkts, 13); for (size_t i = 0; i < 9; i++) { CtrlMap(&ctrl, key_binds[i], key_codes[i], KEY); diff --git a/source/test.cpp b/source/test.cpp deleted file mode 100644 index 15d0df3..0000000 --- a/source/test.cpp +++ /dev/null @@ -1,422 +0,0 @@ -#include -#include -#include -using namespace std; - -void gotoxy(int x, int y); -void setcolor(WORD color); -void setForeGroundAndBackGroundColor(int ForeGroundColor,int BackGroundColor); -void clearscreen(); -void drawpixel( unsigned char x, unsigned char y, unsigned char Color); -void drawpixel2( unsigned char x, unsigned char y, unsigned char Color, char character); -void drawcircle(int x, int y, int a, int b, int color); -void drawline(int x0, int y0, int x1, int y1, int color); -void drawfilledrectangle(unsigned char x1,unsigned char y1,unsigned char x2,unsigned char y2,unsigned char bkcol); -void drawframe(unsigned x,unsigned y,unsigned sx,unsigned sy,unsigned char col,unsigned char col2,char text_[]); -void drawwindow(unsigned x,unsigned y,unsigned sx,unsigned sy,unsigned char col,unsigned char col2,unsigned char bkcol,char text_[]); - -int main() -{ - gotoxy(1,23); - setcolor(7); - clearscreen(); - - cout<<"click anywhere in console window to write - hello world -\n\n\n\n\n\n\n\n\n\n\n\n\n" - "Press Ctrl+C to Exit"; - - HANDLE hout= GetStdHandle(STD_OUTPUT_HANDLE); - HANDLE hin = GetStdHandle(STD_INPUT_HANDLE); - INPUT_RECORD InputRecord; - DWORD Events; - COORD coord; - CONSOLE_CURSOR_INFO cci; - cci.dwSize = 25; - cci.bVisible = FALSE; - SetConsoleCursorInfo(hout, &cci); - SetConsoleMode(hin, ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT); - bool EXITGAME = false; - int buttonX=1, buttonY=1; - - drawpixel(buttonX,buttonY ,1); - gotoxy(buttonX+2,buttonY); - setcolor(3); - cout<<"<----- a button \n"; - - - while( !EXITGAME ) - { - - ReadConsoleInput(hin, &InputRecord, 1, &Events); - - - switch ( InputRecord.EventType ){ - case KEY_EVENT: // keyboard input - - - switch (InputRecord.Event.KeyEvent.wVirtualKeyCode) - { - case VK_ESCAPE: - EXITGAME = TRUE; - break; - - case VK_SPACE: - - break; - - - case VK_RETURN: - - break; - - case VK_LEFT: - // left key move player left - cout<<"VK_LEFT = "<= 0) { - ya -= asq * 2; - thresh -= ya; - wy--; - } - - xa += bsq * 2; - wx++; - - if (xa >= ya) - break; - - - drawpixel(x+wx, y-wy, color); - drawpixel(x-wx, y-wy, color); - drawpixel(x+wx, y+wy, color); - drawpixel(x-wx, y+wy, color); - } - - drawpixel(x+a, y, color); - drawpixel(x-a, y, color); - - wx = a; - wy = 0; - xa = bsq * 2 * a; - - ya = 0; - thresh = bsq / 4 - bsq * a; - - for (;;) { - thresh += ya + asq; - - if (thresh >= 0) { - xa -= bsq * 2; - thresh = thresh - xa; - wx--; - } - - ya += asq * 2; - wy++; - - if (ya > xa) - break; - - drawpixel(x+wx, y-wy, color); - drawpixel(x-wx, y-wy, color); - drawpixel(x+wx, y+wy, color); - drawpixel(x-wx, y+wy, color); - } -} - -//***************************************************************************** - -void drawline(int x0, int y0, int x1, int y1, int color){ - int pix = color; - int dy = y1 - y0; - int dx = x1 - x0; - int stepx, stepy; - - if (dy < 0) { dy = -dy; stepy = -1; } else { stepy = 1; } - if (dx < 0) { dx = -dx; stepx = -1; } else { stepx = 1; } - dy <<= 1; // dy is now 2*dy - dx <<= 1; // dx is now 2*dx - - drawpixel( x0, y0,pix); - if (dx > dy) { - int fraction = dy - (dx >> 1); // same as 2*dy - dx - while (x0 != x1) { - if (fraction >= 0) { - y0 += stepy; - fraction -= dx; // same as fraction -= 2*dx - } - x0 += stepx; - fraction += dy; // same as fraction -= 2*dy - drawpixel( x0, y0,pix); - } - } else { - int fraction = dx - (dy >> 1); - while (y0 != y1) { - if (fraction >= 0) { - x0 += stepx; - fraction -= dy; - } - y0 += stepy; - fraction += dx; - drawpixel( x0, y0,pix); - } - } -} - -//***************************************************************************** - - -void drawframe(unsigned x,unsigned y,unsigned sx,unsigned sy,unsigned char col,unsigned char col2,char text_[]){ - unsigned i,j,m;{ - - m=(sx-x); //differential - j=m/8; //adjust - j=j-1; //more adjustment - gotoxy(x,y);printf("É"); //Top left corner of drawframe - gotoxy(sx,y);printf("»"); //Top right corner of drawframe - gotoxy(x,sy);printf("È"); //Bottom left corner of drawframe - gotoxy(sx,sy);printf("¼"); //Bottom right corner of drawframe - - for (i=x+1;im6p8p=*CXz8~NBXU}!5x$kwa zXFbn4y=$#`;qVWhhi`dSua7wOGFu>Q69m>v5ITebf>IO*3wl8iR&Ly_6B8$F{GyE2 z&}25oH!cIJ8c9#v*pNZvz+66hL9iw+oj0d+4oDyh3Xp=4B>IsJWHTvL9LOG_3^Ahj zSx?9{NM1TTW{}{!~6^}i{{j9sgmaCAvfl|rX*fu1As=UI~7v&&E#fqJ>?Rq-q zmBh^StyeUgM?Sq0Sqi20S|Rp(h~DUxQt|794p)9B`}1~OPeq%?!0lzZq|&-P4w;XU zX%cutkr5GIGQJJg2APLmIf{3rNzwKaX3K8$lGE`37#@}ld6mswRkBe9T~(AJ-z4zi zShuQESRM(J2dUhC%mbPnyxS_)_*!y){hx4nv zj!~4V6k-3e*?2>nN1}@dFp4IH585hKg?Ir_unf&^8-x)+{n5M{h58I&1(fd7cS;c| znG}sX1Wes>MO!CE$fVehthDZI$*gy#O2+Hbx^@j}{yy1dm^xiEHrLE1I^gpYE2Evh zBgMWW=nLP0Vo)l1C^Og|x=%TkUAJsej${>?5B+7@gVLJzB(zPcI;b_Ezml=4Rfg8_ z#^Qz?t3N~H`j1;W|h`iRZ?S{r_|W$*|sYQ+91@= z(&nK6UMsh7GhVSO7|62~!gym#17s98wmqNB%k5^yY>9{FvcTgRE34(&c5p^s&qdd; zL;f7rAXnVnRiEdaK`Y=sf=4yBb#>C08$&&hrxa~fJ3BdbrCXe{_o94|YxlBCB-2zi z>XbO$!_VpAvY!&km>q`>wPe=w$G?GpAxuTvB72`MUoC3sYMVtb}!RmTy zgzTzqqC9%p1&DN~sq!EK@Xw9+^NQ~sGb`DyIkY7YN_g#I*2nKnVmV4?*?nyt=OI% z4{Y1oKG<0n@W-Sodtwcy*P2=H&%25#tX2S#P@H;k@paq^YYnV}sVQty z_RyKR`wW}`fPFg?)Mmu($VI#|! z*mM?Y#l+oKO>bPFx>s=;d|9vCk}BSMGZGtv{{h-IK*P4tsR4tvOE&Rl;Y92u&*#kq zgqcx+?Gt_GXDza&MbT>cU{*CLW>M$BQL1t9 z!f!`nsXuC_Qg&PlTT4&H4QJEna9sUkBFRv z+3Ka!B$NMEE>Yr;UU-N;8PbO)4e8G$S~MgYCpOjN&tLM&3$K4zaiF93CZ;;+`k5H(Hrd5lDLy zV%WE&PK@Zy@3^XG418^w{NLnKuPhTDM*o+3Vch_hL!ap~3ok5zB)3TU z?}C^Vmte$a0~Xx(E@z#c*bDeAS@EVUVcn18eKFxS4Dpt|)R^6Y%rmrR#0X`wvuYu| zGh#;CuZ!j9E(l)V0kyZ}mVtY1Qe1$7+lO#-Ra+aDuu0Jjf&XGDhQG6m>EYC2nr9*L zPm`j&i^0`IP`j&4%fZxnOuVe2z)`hq7@Zn5QT(<)d5@mLHqngHuZrJCl4qKZ9ih=_ zOW1vu18I#OVn+lm){kWe=!iakNMZzh@tvn_FT!AU{-(fLTy5u#oIh>nXB6a<`D8^Q z7ISsAE%x8x;I49aRP}!bUeN8>w^!Qh|1Ys3_Vx<9ga03#Ve4u@caJ7?HOPU3<$QPf z?Xs>0{~K)R(i_NQY{l&T%k9?Nx^P1mmUm%c7w+$hx~&U0xJA8jvk9j__lKW1{L?{E zS^X`q<##Y&h7X?-&HdCcHj?yN`sr#((90Uv@hRw$xAw?u#=G0sye67oEIjlRo2|LUX1joN70CzrPT)T+|B@bMgQon$ zX8Y-jKPQL&2g*3#NyZ5|Lxua@yJ=neKj)h*Y~mOe(2?$LW}wNrv;5*UOH&4Eh7M00 zmY76^xmo`a{xcr`=gYy9uR=?8{sSJwX6~%0!5MJ61~2u10euCb1zZD^kS>FB+=|o& z&anaMXK*X9Z$Cj0cG+R$^+-9ixz~L;2cNA2*N^ej9kx@-aG_$<<1aE=}W z1fdRG4csyS7xL4{Y(Zuvu0ze>9KS(256&?Jx20Ba4R9q=JGTM8j&ui{V+h`{3A^oh z1>6suW8Zj;1h@uRjHCtUxB)2%d_x7u0i-l!>VWM?5;(_UxZ^$q&T%eM0XWCANX6hB z*Ce7b;2e)3Jp;}$|6cSKoa6B%bP$~5lS45Q;0EAZNELO+a54hF3QmJ_oQ>2BUH~jY zIuBj~48rS^%itW7klMgGu0ZMlHvo6yO-?6x4e&Ljus!G)P?LfL&e1kP5aPjs6~e_- zK}bc0W8_Fd$O7kBn}&4&&M`L~>i|3t*nqSUoZ~g5QgCdyLXaK}0M`H;k)8$TxHc2L z2j>`dA9@eYvENuUY!BvN1F{3@GzvJTWTE%q9Pc8X2j@638|wg^<94KWaE>|SunxdE zK857D*N*MrYH*Gp=3p&=_23+1kX``ih(lJ`0nQNztxyXd%E>vTqsVkj z5`j=3c}(qDsrK+@`2x$Mj=BBl%%j1y>QS{N{Lybbk1R~>?T1yS zXXq(bm3$Ad6qN1q*qEg7!9@B&_kNvx%SZXv?5f>Wx2t|v^DdfFp6pSw&tR!6H!;?- zC*Wz3wO9gI4+#WhR9}T=iP|(xWW&&<_3X%tw?%$e*wJ6SAdq2ewAW1@&5H`QMk+iI zxs_&bjS)*^fp%{V6%8`=Dn^;!u6Ax+OT)Hn6gOmZxbjkom^8axEU4N}M}rDpq~0FQ zI(S6IW8920ujjxDWGnHgNHyD{#mgdfY>B3;+e1meL!;1n;3gowHNj?MDf6@nK_5TEgdf{WXjfVabr}-<}a8(RqmV|RpZNNQX_~$ zV(I*qrKr^cL-YZ)GZomHh#d$aznUqmnP%6-Ftl_}IPIv>DwBG-MWJgoT2bS-Igdq* z&Y55O(Bq5d3lb*B?))!v9_PM=txm-eP9rNk+%_H*;l|Q)`y#~40;vy0Q|m4*JzeWd z>-R(`4Lu^}?$$CZJ+V7xL|=bLARc6mC-ww%K^_Ax2+u*;*xO;_2yLbJcNd8QlLI}x zM~mfGu_s2^(j&~fd$gieMf&}b6}#YPu-f70{`o7$mM$$83Y^Mg^Gj#XEh-gmI29iK zAZi8S39t_ZSB^t@)v0hNljf8NxQD?>U(+hpKJHWDy)aft zV_J3|DpCsA>|8cRN_?G9*iH;++3S)ruSeP@WEiY3uhaC96kuG1S9<6@5G?P=uS|KqZ7P?r=TQtDIU)K zxP*${xQmx?j~yveUd9OlBdiMhKK$#EVx?Z`-i*Imw0NaC`)Cf`wrG{rE~QaN)8&%8 z%2pQ@Cj_lNhH;N^cGW;l$BI;~jz@>-!Fo~Do=_%Ic}+BxAIJ8OecQG7Zy(o+HCpsO zo`O$gC=ES|%TCC}nAv+4lq{XU3_Dqk*3oA#&mCyMYljr95)WlIkSwe?yIFxB8p z=%|;J8tnM4;b)%IV(QnN%o(qVwY%k|xT0aWYy(}D5Nz)k<6IRjl*mD@N-~sM3TTL7 zf_&U=Kwb@YkB_6Nb+oFX2(xelHX8Cjm7^@_(V~^7bU08BpNa`;NU;0vK4bIf`l%wk zQk{G{M``U*@APRcZU$eUE>fy-V!8IQGVg9s{U^rcZ}L#g#OPo;nZ#Zzp0hkkfFM_JROKl$%etcBu)Eyt_GRnz4_iI+slWJ>FcT zOzu(fPP10o+M}ZW94U&fRdk;GSE;267qk{J^ZzCP?INkqVp)$8e z&D8g`N<)vDhW91WFj5|x_V*=aOON9G53oyd$C0CZV{;WoIi_v)lEw2^;(}>#Dtu}8 zgUul~WeAiy{y>NEy}s40YzCCD(bW6H!k`%Z|4{duv7(@WKl=%#<$T-1+38k(8Rcb8 z#j!XH^y!C1ET6`Fgu@73w+~Pty>TF1slh$ZF<=3Q!)gCT9eI8%QQRdRtp)D8q}$XY zk@j;Ptp@G}cD7VwQ?Q*cqC1}yiyfoM`j0|t`FpWkxb#zrG+*eb1Nh=)9W6dz&D1os zt%wRQ6w9oTGCx~Ho37~S+Gi4}ztqv(ixNEtYz6v#rK3kKNwfiY8R*@rqlZ72=&!&_ zK+msrH0_IO>}K5Olz$YUPNnA_Q7Ro(^VZ!xFXSNU^B22IPOb{%D>UkS>PSu zeP2mbc1=gtuOzmf0=`V4j8=)#uKQ5Up zF&x@69cV)ywQ7FssaR zJMrHwGJCJHT5OR!^6tNi!Q_s-{9nbm!YFOi*(I8f?^b%t_B`v@`~Zs)J77F_W0EI+ zU&i_Iw*t61V1rZ$w5vMUCj!?F{7>dC>)Pt=GYQ^=pJBTL^j;ZP90tL&Dt#b>Ij4i*!t##v|i{%7q+&ho0 zwZwd(7Y0(FZvLYe+Sn@lcZ~EN#_lEaf|&)x5$RRRo232j17PeDg2JvU+p4 z4+dOti!d#Szw&T&f_Gp(los#45B0;ewYZh9 zM7cW6ywHy&i$a?DWf?Qf=lxhT)0ltrL%nKqf~=r)|)5w#t3$ppXkk=$8usSj82Q2tI!Cza_f97l&}o*UKN^DXKqr#fx&!J z#Vqi%Q;l4b`5g``&EKh65z8|_7=U$9U_KPUR>d~E4fL_Tf^f-g+448|dh-K;EG3w~ zW3``y3sx+BymUSa(#%@|vBL7qAHo1To7ppnB?nz{Yj9&fKEe}&SWIk~(*vK3TPWy$ zp5+r8YO|Sw(7bB%54=W)+w}2Ype0Tp%kU0l{$jjVHKzo#m>C+k)ztvg^-fbB?p-LY zT}Q8dS>1$E=QfnTL9x0i{`f&#W3~ii$yK`*>_CCl>>tAL%E-%H9>T_nacSnaLs)ch Wfiv!crSmaNSV8G0?u4)tn*Rn%?$&Yu delta 11409 zcmd^Fdsvj!y8qS(gTR0@T;wLo2m@jQjfx`*=uje`rG=Jmt<=%P6i`X=l3N8c+tP$h zzO_G3nxbiWE3qj>QX4M?-fFsf$%;%3Z$)-vCp*q>eRFY!_C9}{=Q+=L&U5B@*8JY} zTkm?`cfIR6=&m3Acir%}u8fOSvuVN_L13K(p+!g$)S}o+7%T|F{O30r#f>wcKR2IM z(kM10_)IQXEy`eeiS_O^6q47+BnbA5Ia4Q>PZorla5R`vB2k9X_pB#-h@!-v>=7C) z#&x#)gRVgdK$$|*#QWKF+Jol#^cB7rQh;AiQu&3`5Wgt4n8y0`WJ_r&zE{z!XxZWy zN#FQIveon}YQLb?$3K$&-8RhsJ7!K8BM25@g}ppvp9t&C2|0prEqEW}as=e&rP+cY zS-PC&df`>t=4sCQYwr5`9=u&u@F2Idyp_$aIocB?2F%@qU*>52zA)>+M~gQif=_4je9-O>C)FC{9MVW^&{ zO=A z6hFEvI$2bQ)(M!pd8+1}$dE;~9aU-h(Xt6g#z<9{q~+Ij@O*ZZSDMaySHAYH%%L4Y zKd}_r5Ij&kX{2+(J;fsf$WJ|(JwSJ<$FLV|Yt#c-#RJl^>#2xKs@|!u#8{=OYP$kW zQq>WuI$xJp$JcN@|Kfu?f%Pw0@(p}$mS1I3)r(f%!tBy=yGA@OW^@^2nv0N3lN zR6SI0LIZlPSjp9R$LdI+QXO99=vxn3F?@F>Z+EIy%wW@MF)bIdfz|W30ZvPu zlnmt;!cj$Y!wReo<#T11GJRuoe30B@Cihsa)a|^kS0bpcgf)l8?O%&%%xzjaZ^;G=@MZdYLR08%tzZbdra{|HG7bVj(sZZ7E{bkP1p#%0pCOQsZ}HN zy8J2!X0lqfd6;t`4`yJuE8#MC)NWIJ#}7QLdGftKuwgB#di0nkX=d4*uZHnHJh5!q z0S@c-%ojAP^0;2rd8<^tWh=;sEBJm~cD>LKo?P3ec|tVyb{_gm)%i8ZK9&KJtE>Qv z3fQosk)J2{TpNuM@d|dr3Qi#CHTl@?{-S1Ym;hqBCDmS$yB~1VC<3-V^U#rJ{^e33_qyMM}8Bw zNRwsE^{PTGX<|pR3p61%n%QVxY%+V4*2bpeIz1Mf$qw57hz%A=8=p7mQykIPvat)W z;_|gT%8{^jt(P~mnKRMeOi+7!JJpyXX+wN4d&jmt{w#~_h4WgfE@X-wEN4lx3(HFt zH;~qKFJcxF6B^hFI-W3yt+lCi?}#yNe^)MyDpk@`=xI^Kb6DQmPTiBb(oczzVN2W! z7ndsvUs6m`TIa2(SjMy=RP-U+=p>!kZP7CBd*pd6v!>xy$MWg|`6^O0?tj7dTyie! z-hzp^sTqH0n&wW`b;xr&-IO$G_G{g-IYYmot0_8mmDD}&(7(HyhlM+*lQKWo(&1)D z1ka<@P|SO4aDR&JNMep557KI?*#ZJ`G1m)yI;> z-+_B|-bG7(TUCDRwMbYB??)l2I^8SZwzpexuNiLNYZlczOO>ln)1Z2tMx+kX%`xdx<+7iOT)% ziV2lR-Iac*%y(B(Pm6>U-=S$WS`T%c_rpokyU>wzV z;gk$b<5VnVr*&n&QjfG~nw8d>IUvQ6oEE8>QLQW+Br+Gur0}1nMX)W@oHmYypdQ1` z(WLZ1c8T)Ry9b?tVJ&nIq83B1rN8%j{nGr(~ei{U1Qee`EAUiT5UU$ zVPfox&EJsC5Rs|RUN(VR`_#w0TFxg}qulTo)f&_kZ_m;3z6JVhR2x(S@y$QTIiSIhv(l3=gM<4XN6#9?k+Y8}_&7PUTLRUSGUU<_*;C=Qa z-Rf^-%c-xipdy0v%9;+two;yN22NS>TP2(#PE>W{RJH1InOiwtQFfkR-mBJ7HBxI) z^e{caO8XUlYoPNsPv_U3R6I50w+c9~`Irt;yD^2ylrkVso$S$jn06au3M{HrFi6u= z=*A8GINKe?57_lQWL0OS4_5dv9ct=W`}7<_G@BX0zH^x#5rxrna$?dZsi>E6Lnqb zpy_V*1&MiSy(h-;1YwlTo8bnhOBY-(%~q!z#^uFMIW&OA=RHwTk0oohwkQ8PT;yu^ zMNI;ZUVPe_4u7=Ev_ zJq1X4+oGw=|K-(E9i5d5C}H?Ee_daNkF+%n*R$Bmj`sF7C|gk8L^+Fc66IZ6w|gID zy%N7@Z$EPG&&ACjM;n)C)3y->eQHNKT|0-3KM~CNuj^ZFJw^^;VKuqFY6g1zz6pW7 zbbb0|^vy8P)AyzL&3tqk?Y>VJ)X&4Nyifmsj2-4ZB~=> z=HCHf#N8M@8aQ4;ISQHMj-hCW%<&e=dB_~^&Br=`%<(srpCNPXJq$r^cH%n7A&@yP zDZoyGjB63UzXd@L8CRk(vjSfRRB#^(r%-Yr<1Q4EhGS7e#!V;SmJ=R?%n|pXVCFn< z0!k@lj<^AZg^)So78IU>%<&TxD`bwi6@@jBIpSs%Hbai$!V@U9sML%Uge@rhH`^xr zk7lct3agUX>s=^h(`dGpCT&V$@szhEmey_3tF@inZiaI=ba9hjY*EXFB8$r|C@Oz& z#>^ri1~cTe{+G!!xN%{IhjIIBQM>)U#J$4b-Lz_JoOnhc?XE;>dPR?6R%}U9n>#oh zv02Zo^!Vna0oOf7xr=4~$Q(wX7_@HYv7+)xQ%cH(eIAB;--%w|`Y?OIEWoaC zriL3CIk`*-2zN8Q7yrkkn;wSi6ihEF7t9gfN*_Y$tPk@gn5mK8I@iIR@?qM+4DI69 z@uWi;7@zmv^y90O)jFgQO9AyY%AC)CO|KRLU1?>`IY0QCUbJ`NxyjLDu}D#FqkIJ2 zz@$cdGrho&hjBZGfx%mgRdw7SE!!SVh1&z^1`9#&7K5hDG3_4UC&Ik zWP6gjrbFOIwv#X3SDH`LuAIn{p4vOh4@`!9O8Rdc8nZ#%F}WHes4K2C7X#z6I2Qn2$ z8E?dCX7_NL;v+Frf-Q*_y%81horib3FwiS+l(3!1PJ!C$G0G3XT%3&?c9p1`eArmZ zsMRYiNi@G!QX6m%dL5c{p;j+y6O}+r$AYN$?h?^~gSkGD>UZnal^wLjH}#?}k<5FB z(X2Q1YDb6WJ#R{wxb`=ba7Rp9o6u_)5_crgduZ=~X>u`so}*YtCAM4mSL(;!DiP0! zv}jWz{rXm^T89(UJJzS)=4aQWJq2|0ZN0k8tF_0TT&3lf+U~{j(|YM%BsxhMU+uer zP-@s)qOtS2L8ASb2~sEOv84-ZrN7h_sI@p@ycQ#2O`SE1fLUiXw0(ntGBSS01v7pQAH7}#2`7tMOA!3EG#FR6u4U-yr^qsLlb z_D;cYhs!Jv4(~9eaaT{LBPY`Zr}^Jfy%{~24}6$RFykm}e-cext5ZFLS_`JBYmhro zGcDO)!UVdqpWlC>2MW}M9pYJdz=-x;2a+O+(>o4Rpi2iz*i;&Ius~hYq2K<4dR9Z< z94t|{bZ9AiS1;D2d*-Kx_Ptx8UW_qegqWWY`fz_NQZU?Jpf=%V^PZcx><@{KJ4t&e zG3#-II~tyyc{nlp_VLceDli|p7_S$=om70NShbRSQx4>oLuqPj2QQ|>IMe#NW+)h| zAdbvOf@#qEQPi}{5}0Toprk3Sp-t`ZXgM#?5skP&oy|GO-ulMDY8{>m zUa47qOwr&cV&#b-Hh}IqR;<<|2=_wZ@3Aw-uCiFeBp!Zvxp)Og=98E^Pe(3pKUu7nI`qssrB|Cf^sGE3 ziH3nna;~3})U_R&$9;e^Y9J!&p4}NIYVAPxL`|AKt7swKlZHWVhA;1K(3#-F^af+) zPvn?FF8Q=Q45o1qb^fq8;uD_+I~q*t)GeP0>^DZrrY87eL=T^~wMw)m|qT|5xKWjV5Gz=68V2?q?#*MWUkNE)5>=- z&Am30o&+Ct-AYR@2GPK)k5c%JQe={k@5+stkodTs{a&KBi$)6mL83*MjIDQl1>3VYzb!&cdai}uM+ND?)U1i5{>=dNYj6l=u_Zj;OHNWG__SC2k-)L*i9pu{~^&) z;8(ywzZmI}-zBO8o&y?xHPX0S61@fd9N70aBR$wAQ7!OeU`DHv3jZn5PT&W?JN{v$ z5r0Uu6?ps)t5})Km*4yCrI7e~dT~7~WtFm;vGHuBT*SdDzsuNjtVX_HWJzKTI!wWD zcm4}AHH80Zz4&k>z}OkX_$m3hE-ws zuU-C`1;#A*QL!Zvc07P@JMqV5JY7DdV*S}T`8O3y>JY?Sv}@(`0G7mJo1~5i@$tNAWIY*bL3eZTXW>qfh-e~(GZBK(aAl60IBj{gIGa?Bggv~n}x%+ zQ2s0kiEPP{e-Gj@DhlpQ_d(ukJW)@cGJ6g`3(MpM!94f=^15I)js?oUpvtDp`0tke zlWToEjEz8m9s_&;MPO>>xoWl~rOrn+Iuf($dVD(PrU#hjY&j|fPHN=YA&6h_N!S%A znB`+=5NmVg@KER#$|a$!B)QVZXDEJ?I(!lr1EyBK6^hwiBlqcyX{+=JW+B?`+4A(x zEK{t_me(toD}UOVB{D(&tuy+S$>|zaz!u8$IGE%E8a95w86PV$epO07%pNcXALbaC zEP13B8LW{X*J2mN$Se%msq|T=QE@QkGx%gMjk)sXFoa~58^U0~AYTh(HrUw^j+$0J z!XZ`uE}Y@%DnAf`bzqWTk6=rZxB4Xd?RflM)F(f7Fgp4ENY+1+|Dn}+^G#bYXGVDu zCNx`K6NwdOkWWEDY|4@SyRgiNl|BI<>c%(eyGux+vk@ z;5{5Z)3*%2ICY*V=HkyFMY9B9tK2_|B|XgFK(}{oJyf$is(ih@4~G98<+jc>FlL`r z{syMZhY7_`+CteDg(YY5X;_Db8aXtYm55C$c~LYQCZ5Tb4@a}aNUg{3v^hmc6a41v KZH;Dob^i$~@pg*<