From 315e18e66cea20bd426be9b05337f53c9055e0c7 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Wed, 15 Aug 2018 14:03:20 +0200 Subject: [PATCH 1/1] ISO15693 device side improvements (#652) * ISO15693 device side improvements * increase accuracy by doubling the sample frequency (hi_read_rx_xcorr.v) * adjust armsrc/iso15693.c and client/cmdhf15.c accordingly * use more accurate approximation for sqrt(ci^2 + cq^2) * improve EOF detection (was often mistaken for Logic0, resulting in "error, uneven octet! (extra bits!)") * hi_read_r_xcorr.v: avoid overflows during accumulation and truncation * explicitely cast unsigned ADC samples to signed --- armsrc/iso15693.c | 126 +++++++++++++++++++------------------- client/cmdhf15.c | 49 ++++++++------- fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/hi_read_rx_xcorr.v | 131 ++++++++++++++++++++++++++-------------- 4 files changed, 175 insertions(+), 131 deletions(-) diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 1f0b8193..ad6f5cfc 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -81,7 +81,10 @@ #define AddCrc(data,datalen) Iso15693AddCrc(data,datalen) #define sprintUID(target,uid) Iso15693sprintUID(target,uid) -int DEBUG=0; +// approximate amplitude=sqrt(ci^2+cq^2) +#define AMPLITUDE(ci, cq) (MAX(ABS(ci), ABS(cq)) + (MIN(ABS(ci), ABS(cq))>>1)) + +static int DEBUG = 0; // --------------------------- @@ -303,13 +306,9 @@ static int GetIso15693AnswerFromTag(uint8_t *receivedResponse, int maxLen, int * // NOW READ RESPONSE FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - //spindelay(60); // greg - experiment to get rid of some of the 0 byte/failed reads c = 0; getNext = false; for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x43; - } if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { int8_t b; b = (int8_t)AT91C_BASE_SSC->SSC_RHR; @@ -319,11 +318,11 @@ static int GetIso15693AnswerFromTag(uint8_t *receivedResponse, int maxLen, int * // every other is Q. We just want power, so abs(I) + abs(Q) is // close to what we want. if(getNext) { - uint8_t r = ABS(b) + ABS(prev); + uint8_t r = AMPLITUDE(b, prev); - dest[c++] = (uint8_t)r; + dest[c++] = r; - if(c >= 2000) { + if(c >= 4000) { break; } } else { @@ -341,12 +340,10 @@ static int GetIso15693AnswerFromTag(uint8_t *receivedResponse, int maxLen, int * int i, j; int max = 0, maxPos=0; - int skip = 4; - - // if(GraphTraceLen < 1000) return; // THIS CHECKS FOR A BUFFER TO SMALL + int skip = 2; // First, correlate for SOF - for(i = 0; i < 100; i++) { + for(i = 0; i < 200; i++) { // usually, SOF is found around i = 60 int corr = 0; for(j = 0; j < arraylen(FrameSOF); j += skip) { corr += FrameSOF[j]*dest[i+(j/skip)]; @@ -356,7 +353,7 @@ static int GetIso15693AnswerFromTag(uint8_t *receivedResponse, int maxLen, int * maxPos = i; } } - // Dbprintf("SOF at %d, correlation %d", maxPos,max/(arraylen(FrameSOF)/skip)); + if (DEBUG) Dbprintf("SOF at %d, correlation %d", maxPos, max/(arraylen(FrameSOF)/skip)); int k = 0; // this will be our return value @@ -370,10 +367,15 @@ static int GetIso15693AnswerFromTag(uint8_t *receivedResponse, int maxLen, int * memset(outBuf, 0, sizeof(outBuf)); uint8_t mask = 0x01; for(;;) { - int corr0 = 0, corr1 = 0, corrEOF = 0; + int corr0 = 0, corr00 = 0, corr01 = 0, corr1 = 0, corrEOF = 0; for(j = 0; j < arraylen(Logic0); j += skip) { corr0 += Logic0[j]*dest[i+(j/skip)]; } + corr01 = corr00 = corr0; + for(j = 0; j < arraylen(Logic0); j += skip) { + corr00 += Logic0[j]*dest[i+arraylen(Logic0)/skip+(j/skip)]; + corr01 += Logic1[j]*dest[i+arraylen(Logic0)/skip+(j/skip)]; + } for(j = 0; j < arraylen(Logic1); j += skip) { corr1 += Logic1[j]*dest[i+(j/skip)]; } @@ -381,11 +383,14 @@ static int GetIso15693AnswerFromTag(uint8_t *receivedResponse, int maxLen, int * corrEOF += FrameEOF[j]*dest[i+(j/skip)]; } // Even things out by the length of the target waveform. + corr00 *= 2; + corr01 *= 2; corr0 *= 4; corr1 *= 4; - if(corrEOF > corr1 && corrEOF > corr0) { - // Dbprintf("EOF at %d", i); + if(corrEOF > corr1 && corrEOF > corr00 && corrEOF > corr01) { + if (DEBUG) Dbprintf("EOF at %d, correlation %d (corr01: %d, corr00: %d, corr1: %d, corr0: %d)", + i, corrEOF, corr01, corr00, corr1, corr0); break; } else if(corr1 > corr0) { i += arraylen(Logic1)/skip; @@ -398,7 +403,7 @@ static int GetIso15693AnswerFromTag(uint8_t *receivedResponse, int maxLen, int * k++; mask = 0x01; } - if((i+(int)arraylen(FrameEOF)) >= 2000) { + if((i+(int)arraylen(FrameEOF)/skip) >= 4000) { DbpString("ran off end!"); break; } @@ -446,9 +451,6 @@ static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int c = 0; getNext = false; for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x43; - } if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { int8_t b = (int8_t)AT91C_BASE_SSC->SSC_RHR; @@ -457,11 +459,11 @@ static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int // every other is Q. We just want power, so abs(I) + abs(Q) is // close to what we want. if(getNext) { - uint8_t r = ABS(b) + ABS(prev); + uint8_t r = AMPLITUDE(b, prev); - dest[c++] = (uint8_t)r; + dest[c++] = r; - if(c >= 20000) { + if(c >= BIGBUF_SIZE) { break; } } else { @@ -479,12 +481,10 @@ static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int int i, j; int max = 0, maxPos=0; - int skip = 4; - -// if(GraphTraceLen < 1000) return; // THIS CHECKS FOR A BUFFER TO SMALL + int skip = 2; // First, correlate for SOF - for(i = 0; i < 19000; i++) { + for(i = 0; i < 38000; i++) { int corr = 0; for(j = 0; j < arraylen(FrameSOF); j += skip) { corr += FrameSOF[j]*dest[i+(j/skip)]; @@ -494,7 +494,7 @@ static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int maxPos = i; } } -// DbpString("SOF at %d, correlation %d", maxPos,max/(arraylen(FrameSOF)/skip)); + if (DEBUG) Dbprintf("SOF at %d, correlation %d", maxPos,max/(arraylen(FrameSOF)/skip)); int k = 0; // this will be our return value @@ -508,10 +508,15 @@ static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int memset(outBuf, 0, sizeof(outBuf)); uint8_t mask = 0x01; for(;;) { - int corr0 = 0, corr1 = 0, corrEOF = 0; + int corr0 = 0, corr00 = 0, corr01 = 0, corr1 = 0, corrEOF = 0; for(j = 0; j < arraylen(Logic0); j += skip) { corr0 += Logic0[j]*dest[i+(j/skip)]; } + corr01 = corr00 = corr0; + for(j = 0; j < arraylen(Logic0); j += skip) { + corr00 += Logic0[j]*dest[i+arraylen(Logic0)/skip+(j/skip)]; + corr01 += Logic1[j]*dest[i+arraylen(Logic0)/skip+(j/skip)]; + } for(j = 0; j < arraylen(Logic1); j += skip) { corr1 += Logic1[j]*dest[i+(j/skip)]; } @@ -519,11 +524,14 @@ static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int corrEOF += FrameEOF[j]*dest[i+(j/skip)]; } // Even things out by the length of the target waveform. + corr00 *= 2; + corr01 *= 2; corr0 *= 4; corr1 *= 4; - if(corrEOF > corr1 && corrEOF > corr0) { - // DbpString("EOF at %d", i); + if(corrEOF > corr1 && corrEOF > corr00 && corrEOF > corr01) { + if (DEBUG) Dbprintf("EOF at %d, correlation %d (corr01: %d, corr00: %d, corr1: %d, corr0: %d)", + i, corrEOF, corr01, corr00, corr1, corr0); break; } else if(corr1 > corr0) { i += arraylen(Logic1)/skip; @@ -536,7 +544,7 @@ static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int k++; mask = 0x01; } - if((i+(int)arraylen(FrameEOF)) >= 2000) { + if((i+(int)arraylen(FrameEOF)/skip) >= BIGBUF_SIZE) { DbpString("ran off end!"); break; } @@ -602,10 +610,6 @@ void AcquireRawAdcSamplesIso15693(void) break; } } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; - (void)r; - } WDT_HIT(); } @@ -614,9 +618,6 @@ void AcquireRawAdcSamplesIso15693(void) c = 0; getNext = false; for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x43; - } if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { int8_t b; b = (int8_t)AT91C_BASE_SSC->SSC_RHR; @@ -626,11 +627,11 @@ void AcquireRawAdcSamplesIso15693(void) // every other is Q. We just want power, so abs(I) + abs(Q) is // close to what we want. if(getNext) { - uint8_t r = ABS(b) + ABS(prev); + uint8_t r = AMPLITUDE(b, prev); - dest[c++] = (uint8_t)r; + dest[c++] = r; - if(c >= 2000) { + if(c >= 4000) { break; } } else { @@ -668,9 +669,6 @@ void RecordRawAdcSamplesIso15693(void) c = 0; getNext = false; for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x43; - } if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { int8_t b; b = (int8_t)AT91C_BASE_SSC->SSC_RHR; @@ -680,11 +678,11 @@ void RecordRawAdcSamplesIso15693(void) // every other is Q. We just want power, so abs(I) + abs(Q) is // close to what we want. if(getNext) { - uint8_t r = ABS(b) + ABS(prev); + uint8_t r = AMPLITUDE(b, prev); - dest[c++] = (uint8_t)r; + dest[c++] = r; - if(c >= 7000) { + if(c >= 14000) { break; } } else { @@ -836,7 +834,7 @@ int SendDataTag(uint8_t *send, int sendlen, int init, int speed, uint8_t **recv) if (init) Iso15693InitReader(); int answerLen=0; - uint8_t *answer = BigBuf_get_addr() + 3660; + uint8_t *answer = BigBuf_get_addr() + 4000; if (recv != NULL) memset(answer, 0, 100); if (!speed) { @@ -957,7 +955,7 @@ void ReaderIso15693(uint32_t parameter) int answerLen1 = 0; int answerLen2 = 0; - int answerLen3 = 0; + // int answerLen3 = 0; int i = 0; int samples = 0; int tsamples = 0; @@ -967,11 +965,11 @@ void ReaderIso15693(uint32_t parameter) FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - uint8_t *answer1 = BigBuf_get_addr() + 3660; - uint8_t *answer2 = BigBuf_get_addr() + 3760; - uint8_t *answer3 = BigBuf_get_addr() + 3860; + uint8_t *answer1 = BigBuf_get_addr() + 4000; + uint8_t *answer2 = BigBuf_get_addr() + 4100; + // uint8_t *answer3 = BigBuf_get_addr() + 4200; // Blank arrays - memset(answer1, 0x00, 300); + memset(answer1, 0x00, 200); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Setup SSC @@ -1025,21 +1023,21 @@ void ReaderIso15693(uint32_t parameter) TagUID[3],TagUID[2],TagUID[1],TagUID[0]); - Dbprintf("%d octets read from SELECT request:", answerLen2); - DbdecodeIso15693Answer(answerLen2,answer2); - Dbhexdump(answerLen2,answer2,true); + // Dbprintf("%d octets read from SELECT request:", answerLen2); + // DbdecodeIso15693Answer(answerLen2,answer2); + // Dbhexdump(answerLen2,answer2,true); - Dbprintf("%d octets read from XXX request:", answerLen3); - DbdecodeIso15693Answer(answerLen3,answer3); - Dbhexdump(answerLen3,answer3,true); + // Dbprintf("%d octets read from XXX request:", answerLen3); + // DbdecodeIso15693Answer(answerLen3,answer3); + // Dbhexdump(answerLen3,answer3,true); // read all pages if (answerLen1>=12 && DEBUG) { i=0; while (i<32) { // sanity check, assume max 32 pages BuildReadBlockRequest(TagUID,i); - TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); - answerLen2 = GetIso15693AnswerFromTag(answer2, 100, &samples, &elapsed); + TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); + answerLen2 = GetIso15693AnswerFromTag(answer2, 100, &samples, &elapsed); if (answerLen2>0) { Dbprintf("READ SINGLE BLOCK %d returned %d octets:",i,answerLen2); DbdecodeIso15693Answer(answerLen2,answer2); @@ -1073,7 +1071,7 @@ void SimTagIso15693(uint32_t parameter, uint8_t *uid) FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - uint8_t *buf = BigBuf_get_addr() + 3660; + uint8_t *buf = BigBuf_get_addr() + 4000; memset(buf, 0x00, 100); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -1177,7 +1175,7 @@ void DirectTag15693Command(uint32_t datalen,uint32_t speed, uint32_t recv, uint8 if (recv) { LED_B_ON(); - cmd_send(CMD_ACK,recvlen>48?48:recvlen,0,0,recvbuf,48); + cmd_send(CMD_ACK,recvlen>48?48:recvlen,0,0,recvbuf,48); LED_B_OFF(); if (DEBUG) { diff --git a/client/cmdhf15.c b/client/cmdhf15.c index 08cc3b15..b6a84c1c 100644 --- a/client/cmdhf15.c +++ b/client/cmdhf15.c @@ -268,7 +268,7 @@ static char* TagErrorStr(uint8_t error) { case 0x02: return "The command is not recognised"; case 0x03: return "The option is not supported."; case 0x0f: return "Unknown error."; - case 0x10: return "The specified block is not available (doesn’t exist)."; + case 0x10: return "The specified block is not available (doesn't exist)."; case 0x11: return "The specified block is already -locked and thus cannot be locked again"; case 0x12: return "The specified block is locked and its content cannot be changed."; case 0x13: return "The specified block was not successfully programmed."; @@ -286,12 +286,12 @@ int CmdHF15Demod(const char *Cmd) int i, j; int max = 0, maxPos = 0; - int skip = 4; + int skip = 2; - if (GraphTraceLen < 1000) return 0; + if (GraphTraceLen < 2000) return 0; // First, correlate for SOF - for (i = 0; i < 100; i++) { + for (i = 0; i < 200; i++) { int corr = 0; for (j = 0; j < arraylen(FrameSOF); j += skip) { corr += FrameSOF[j] * GraphBuffer[i + (j / skip)]; @@ -310,23 +310,30 @@ int CmdHF15Demod(const char *Cmd) memset(outBuf, 0, sizeof(outBuf)); uint8_t mask = 0x01; for (;;) { - int corr0 = 0, corr1 = 0, corrEOF = 0; - for (j = 0; j < arraylen(Logic0); j += skip) { - corr0 += Logic0[j] * GraphBuffer[i + (j / skip)]; - } - for (j = 0; j < arraylen(Logic1); j += skip) { - corr1 += Logic1[j] * GraphBuffer[i + (j / skip)]; - } - for (j = 0; j < arraylen(FrameEOF); j += skip) { - corrEOF += FrameEOF[j] * GraphBuffer[i + (j / skip)]; - } - // Even things out by the length of the target waveform. - corr0 *= 4; - corr1 *= 4; - - if (corrEOF > corr1 && corrEOF > corr0) { - PrintAndLog("EOF at %d", i); - break; + int corr0 = 0, corr00 = 0, corr01 = 0, corr1 = 0, corrEOF = 0; + for(j = 0; j < arraylen(Logic0); j += skip) { + corr0 += Logic0[j]*GraphBuffer[i+(j/skip)]; + } + corr01 = corr00 = corr0; + for(j = 0; j < arraylen(Logic0); j += skip) { + corr00 += Logic0[j]*GraphBuffer[i+arraylen(Logic0)/skip+(j/skip)]; + corr01 += Logic1[j]*GraphBuffer[i+arraylen(Logic0)/skip+(j/skip)]; + } + for(j = 0; j < arraylen(Logic1); j += skip) { + corr1 += Logic1[j]*GraphBuffer[i+(j/skip)]; + } + for(j = 0; j < arraylen(FrameEOF); j += skip) { + corrEOF += FrameEOF[j]*GraphBuffer[i+(j/skip)]; + } + // Even things out by the length of the target waveform. + corr00 *= 2; + corr01 *= 2; + corr0 *= 4; + corr1 *= 4; + + if(corrEOF > corr1 && corrEOF > corr00 && corrEOF > corr01) { + PrintAndLog("EOF at %d", i); + break; } else if (corr1 > corr0) { i += arraylen(Logic1) / skip; outBuf[k] |= mask; diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index 229151dfbf50cb8de730a85446ccbc18407e8474..939ba93a54ce00e9425a1156d3900f1af58c4c8e 100644 GIT binary patch literal 42175 zcmeIb4|G)LbuYT-J4f=7X2cwX@KRz-jz+RGCL?JO1QQI>hrsS6reTsgExju%Z%9ZV z?INk1=JmO0?!BKz0tT?n*zrq9OMAzdB*fvigTOZa6Z;^`j=%yduA4_3>WsOHEjzL! zJH!?s%=_){%$ym4lbg5J?RsymT3xH&Ir=!~`?vRR|MuSBC#nfgdjCfhSWk2Qtml7U z|G#bgv(}zX>%Z{D4Q*fk!Up<0)pq>VqWE9mx-cH6FHo=}zHn)Kd};f_MYMrxIuAUn#u2~e72ad-J-<(z`rq{)(|gbF(`L@^e2@O+&+ULi392Vk z1yZz*uB3pXjdT@NyMb)ikW2V+tKOxHfv!)4r;IZ+O^XV#DdSyw7DDKpF-bk#M%i!B zqtw1ltn!K1aQ~Vy9`?_6OvPa*Iaou#e~;W6Wlh6~yDWO&keH#J3FW1pNfrbxd>+WYCO+c6l<8WZAK zw<8-KFed0cb+}=EkHeoDu5##O6mf$C-D9ec7EtgpF@lle$)tFTB)2J4t)T;Cy1_Vp zm9`wI1>Ghtrm)5E;{@i@1L~Tw;5zYh+MKXbAn$(^6ETYuy0r$cCi+}vAbN1Yf4_At|17c)hTYXuj+L@ z3TuH8qbZrUFh8$5!x-1q2aMOe^-9d%EgzyL*IFs&(E+Q;jP{6A(4!_9>JiU*^X$?; z(g1pB4U73UI`7onCVt{<cyZoBXqbf zMN|7a?ypnE{d7!qm@#Ew$MhU(n5T?44888Vt{$Tcdsay7v3Ja~vh(8*-cT3fH^}f^ z#>>?`UVrw|1zN;yCg}J+2&2Xs`>2d#T&te;)@!LfPLI$Y3g)_9Geb)#s6;_*zc)fr zB~Ex_x6C2zLu^0Ruiml{kqJ98=;>GE+`aN)iX?)&#F*ITTq}cFF{HM&==pitTZ>%7 z7@d_Jsc^P=jE*{A`7}EA3)&|0JkR5venn|7)}pg}XWn>rF$8oTjo(anCZ5e2$2|Rt z+E`OLKw)0WVW(rZsSHOA<4H&v$4rOE9yO!%h-%IT_jZrd7HaOb##eofwg)1;(VRHu ztwj|2^(Z;nt7eI3C@mdjIgv2+k>g^I9V_YAJbFkrrFX0rKc%hIyg&MNaf<`N&Gd-z z;(M(5bOF0JoalS8`$=^m7rM<@CZ1Fa3lLuO^vk*$Hi0@7`pl+axq|8yAap(tn=n|N z;y%l&7wK8GC>M6cIr$?N#*Me>+4!8v(9|mO)@$GF-L#pSMuRDFa&T+lqbm3nF{O5* zKUR-eP_$$8nDZUFPNB{7))s0~!Ja=JwYSFC526iB27fEnaGdU=ed;Pl49dP-OTV@) zJER`F<*oa^+5bepq@^mj-FVr4Cb3ANs}r@)(m~k6VDVduCe&4QW%fh;7}u1<0Piuz z)Oo9uZC%0BuYf&`v3IqEhK{9=VO+y> zkiN@nTGTJHHqqHYr-GGO%}c=J;4H2;Vdcu(PrXTA(%5AI;8bJ?ZZqTVE z%2SIB-YY_s9(G)G-p8-~Ja#vjOFPbCE7BcI*T!f&EkOU$Mf{R;d23wKMPwOW=kJ#%VT;DF`o%x1wF{&KdZn-_x@7rO>eKSQqLwHe~rd;Rnn3 z74!PDUldeELCsr)skYM+^hZ?Smmb&0#E9sl+yTc-|ABy_xxto7{CY^W7J|0|zqSrH z?XeQ#O4_QfnTcQYYg+Dx7m1O1Pl~t-5f*MR8h67^8NVLmt>uQ_GN$E4yK8S)LU;6& z3j8Y0PhQ068P$;s<&2mE1M6OqH_G@$4^b=6e27hfvQhvtD3KfdY9)SgpKETb{#*!M z%~{df`Lso?F9g3PeEjm(^s0Py4DV@yZ4t9bhKtGTq;S9C;}`5Io65V57IoBagUt+nQ5?;(@$~Tv zZZ7mo8d(F7i6?eRzchY%2Y{L_`SW5}b<)mUC4QaFp@+MTSHxM_nZ#cGSNe^s z`|RUaaa^N;#Q-w=>V@DEb(}hQTt0pkpjK^wt<}&kg3vFTC_@VZ0J0K(RRO=E+{0Rv zZ2~$kOxa5*ug@M<&fTl$2e7rt6Ydc$fo-&afvkjIbBq1S8ZF+0+eV8xs{6C5gkOQZ z-mkL8Uh^!Q!EE!8b=K*WJ7?k-0l(6QV-E9DZZRBKsb&16gkM#_FN&yOL41tvlx^u? zA-z=Hxf)OQV6RBF;f!AgVIhP2#TAZHF{fs+s8fF*hb38zHc4)i3jn_qc5fYUz z*&Y&oldf@hq#{N9+6Vl)2(*GWEfYUdD_ZJq3tlN+R4WSMzp2D8Z4>gwIB(ivwwdN@ zw2W;+5xoDJ9|ZGgE=vEmqp8&sZW9XisS?@|L$|0kSU!Ki`>qzG%*5V#ChJNw5D)5WZ3Z`-?9cSl1 z+joXW_{n4ieqmhj-Vb-z(vC!1D)^7$G-cd9?v5X(ef(mZi8i~SdfT10B%CE~b1mD< z5`LK>Z%wJzH2Ww{A-#~c+j|LU(8~FdE*pz z-4hsTu!4V`PjzN78xy_7O$&pM84K;jdmP>y!-3_{uQoMHi67Bh^8l=0Grmqc#C>jE z&iIO_Uz&e)Z(<8D7&s`QR!c-Eumig;sv=8E{^5t{Q2I-$TBb1H0Vo6k3ZgIDRHzvi z3GY43zkW?I3;b(H13i4d5Isq87eH2z9gQD(^<(zCY$_+23)>%J+VyV&EBJIcS>|7u zpW%+9u=B6+y1U`SMyq7fsA$Sq=6x$cFTpMRCWTdBLBc-_=)F2=FQ=WomH1U$ zrdOP^5tz!mFj3%@GL$uh_bp|9ksg*ntE?EKhjS1>G&IS|WQF7NFNZM>ZGKU-;q_h{ zaI|V~Ex^B|#;@6eNezR77~0e8I0@^XT9_mLIb8RJ+_~}21wdF2e8U-|pj_DR8SUsh-LK=uC4c%D|!d}%ZoWAycjdRfG3)O?AVy#}9Ctxn)Oz}r3s|5qU*c_i()u2KiLUCY z9-(j1HN!P;iH+WSfL{Wz#UA5}_MuFx6HXW}nm;NWd}-Eg#sZ<&)Vg|~w)60W$JE&r z>^!&(8~o95e3g%1@6fw0(CVylktVX&$7fA(JnG1zUx%jpeEyZC2a~K;>67wlx{lx$ zo&reS>RJ{2E9?1?J&{w+e_!21wJC8*Znc|+tgoUs4tL0DaM=_*7hD!O;oPO71pOKz zwk_Z(CH@8cg37qzY2y_dcRS=P5P1Rq<;Hr-{A--1EcnKZ%fGa-xQ7trIj@$vJDJpI zJ!SrNL3O#I$t%v#yQ)iuMlp7_hq1gtt96@Rb0h1QJ3J{~PCTk&32V35L@>fftV!XN z_*V-ZcG{S2jnU!Am&rODneh6YhYm^}mt``AiSZ~9I5Rg9gf-$Dz%MIb(l0`M1OBRh zk99uKNilC+nh%nvU%9Ipt-9R0NtpL{*f5T2{-t@!CH$+d$C&PUwyAwM{8QsaI^!C5r%CXpC{atq^A`v`@{z zFaA~js);z;dCbp#`YGdAzPW^7RqRW|hJ%yaSM=_rcAm_bxlKjf(8Hp`TMNc7jH?jr zz^3^IFA4{6zD;}G>poiR-Hka|s^EckSd76H;4!GQ7Fe8J%ai$W#w4INOm^v>zhGAD*%wMu6 za|npm!}|yELr-_UBIe}|Qqxc{DH_-bbYTH1;)lA=e=Ft>Ka^|Y;OFP)d#b4r{JN;* zUjcZ7@!Fz8wYYW@^+~62)zWhO(Boh5d!yDlge1c+85`*hyJH`K%#R-yA)wzt{E(Id z$c!9K*^krOiuhrX7wl?gUZ7!X4C^%@L-~LoKlJ!laChVyY`;AcL5yEeuXK`i_rT!W zJa!#FWc<2odrJaa6rsiRFX-DV5p}$02LJM4s|xsqKw?AnQ?yM+lGim<7yZK$|HAY} z=x}d`6C=Z=QR<)&Hl(Lt96!{Y@t)u-1T08~Pl7MQN~FTOR?XmFT@qV#aS6z(=!mRe z4P36kFHO7P4N`{jYrEK!&gAwu(POn0_*H~jP(|*1c1wfW>@+26lj#zEY5w(P{HnGE zu#QQ%`^s;co6YsIHYqCbi~GD5cnaDDkg5j}1OG~Wa#ypDUq$|98$;dzcLA62uM%~Y z_@yJjSW4!nofPUfav)*@bh}%|FXmrunc!=+OBDmOl@kkq)Js^X_4LByU(hc*U5ogJ zHhL$3Uo8bITE;K%FKU%R61Ke+%Gfw5zV6&X*Nj>lMAUl^^RIW9Z6#`J=|{aQ3bBpG z3*u>YeJ(Z?^zjS0%tPk*;iGIE9Alq7MUN|3ejmS>e{~hY!^YR>Sp*E=*uTw0Y(1X1 zo_;a^LVt#WsajilYLz}CH``4zxKh04`3Xct9hAam)k9-33ZJowplEIhzt}&7sU*;U zhU}|a#31dmot9u@_pqrkh547(cN=cuGw}JL=5L6fN$9)NSNs;@hoy0S-$q;%M$ed# zuR0iDt`fhBTxKa61IP+}x542=KytE-Uz^xI%V1`~0?CFtR1l5uSesRF(nyu?i)|)M zcSc-iJsJRz#l;5r^AUL!?_S9|Xqjv-cwE(%7NT*)0eYcmh$nOhxCs&XmtF$iKj-;d zIU_}gj}bg8AHRzHO9n^TFdq42LG!OC2-@W1*V{DG3;uP|=%nLn(O&T7N%5oHA_^5s z_~qJTfSX7X?m>(N1t2?3J4YjgfKm~^hP8j_tVWvb_7a~frUd0Y#fH#^m=yfF*}iQ9Nm#4i{4h2b!aw$P(1W9Sd?6zrJr z#K*48alyOJA%J)hzw&|kw6p&@2fp8Yx<1pI18luWh9JNEM=dHDlG1CI;fm8i0#yS`o;X~MY$wv-H(9au^iMPXPl>T zYTk>)fzQ7RV%%Crt#Hx3uptD;bK3jL@k0r7%K1t*r)>$C_c6AR8$))m#J^|*AzGLW zHW)Y3Ph~8HZ8OFZ`yL1-{uM9-+bp!XMO8xVr8;9o;@R4#i8!@;<0od_GFW>tz>rt5;PG2 z8Jy(rn-SOgzbf!c95I*4&@Q7TbrfoaK97m-(?BZZ^RJD8?|HCwxA8JD7Y=6^4XK~n z%M;;U75qz_^w7MCM$CEwC=}qFbkv7b5Ft+fiB?5vyMUVKfHH`;WP&;Hug75zt$mgF z^^Ja~07gHH_9%=iUO$5O=HM*@iIV)DTwrNvt85wtbGe$fV1U@qr+}1A$R(wH{JN4} zR0ngh@!&G?D|Mi-<2Lx=Z}%=r#cnI(SB}m>^{_<|XJhGQ8$)&mGlTS=(ZUWbWT{V96TMqqQaod3Fq4pA#vQcTk3 z-d6W2?(>7xYQZK{@UO!%26hSk`hIqktMzL@#R!XA=3frDuxlOen}B)mltJ$E0C(Q5 zmE?>pj9<*ZU=Q6@lZd^;40?SAeoY2_{Cbz(R#>m8V4RL; z7Zo&+p?)J3+6b`g;ZLD{!v_C4Vr3$AZqeX-mLEH_;Mp<64`I~-$2mdqJ}1R%5z^7CYZ$GP!A3DCcYtC*2U;gFr<6f+SFI?73?5>$h9Z{F1_~!+KnuJU@Q?a5p_*p?*WAP2iVKD{BwdMf|Ydn`e$6K9On4B{qur z=FY)u3bhQr%*d>bz%PftrQ?UnQE;DTrQtrg*2gpvi;Y>iNCkeKlhBLr2CqXJZi$+e z!@{wZfKOe$E@^lXenJ!IWA`AYwj#aSyTUY~vOBjp|H6cVO_4xa$ zcyq$~^i2L0lNM6y>Hsj#8m(pC-JHmPe>J3dO>+%<>1?Qz*&n(}12WXe{h_#wWsRum za2u}QIG^o&GJJR68>S|hck8uyoWbsxw>P}{jZPc$gK1&@#fFF{DpclQCiqvTO(n*m zUmU21nlFG6W<-l1O|HL%_y%H4FJX@zcMj4f56HfufM`Ddasu=0L)1P9EI36EIjsz2 z^Bf>r7W}K|A4b8y&ZrglS{scQop&-_3}hdqpDWZkOa*I!82I7(jj2?p1*k=NgY5yC z0FWsdha!H-x$n}n+cl}nHeg#A$d=G?;9xirv^{&c?*qFfu<0kWR&sx=R%s>0V%ltf zl!CI1U%MH<6rTK;$r@QMh}dSjMg~uo@GE7GP~M3-wJidHM8Fn+j0iDd6Zq9=YTRM| zHASFQCNTG+f|?)hi-}`Ue2~YA{MQthd+j$~gGuRh+w%zWoRfNhK7Juj>GcGP32oU{}n9a7wR{#{o3Q!UU6^sQEKPJCD^I_hzd@Y@r&CahNikT5%YW`Y+B}4 zj^}SM{~9oX7Y@i9h?vyARv!BbZA6t`3#!?qw@pmJTwJ8(_?1zxrbs(fB$C{S>o*WT zq%hMi^qEi(pd*T!e^qJya;qkDr=r)>?I%~oX@uL{#x_%R72tUI`1KNHBzD~{Y`^jr;952{w?G9DH{81~+iuwwKo<&&Y=h{+&^Cc_Jj zkkS<$74?UQXpLGba5 zqw#TxaShQC)MtjWbH-p-+4=YDH_Q!U8|?uSMP3#)!w&d5+o# zU@jKfK&w%jc5dKl@qi3MseU5>+GRylC?{UC9&uqez{A16+7yV`F>ft6euz-ak(#CA z1Z;TpOwhp|Lza>Em|wqPQ$O8H)@m{89FnbsSe)iEF81@d_a2TPKHIyf(7JKXbLJWK zH@Pr?44(Ol0u0(~g1Zd-%GGMtpEV}f6eO`lHDPhX`74U}#q}FNtCuuv!L}^^(4PnY z3!PAK?5D74^*ryJeOLFLsV0Iq*eG?ORH_>FQTDw<=ububaI?rg9bG9J=^?vS-kj>@ z8mfD_&wl*yCE6~XdhElhOr8^v4s1)B93HzLKgCPP5j1YWCZsX(EWcM3%1BH06PFr0jKw$#$Ll~LKa{b1C1%GdLqx;3H z+6?Z9ugfxmB8Aa-Urz$?KJ?n+DFHhw=-6Eun#In%?&M?DMaa8n$K)B3yA? zq5hBqiR^e(;@7We+fdtAGv5|Up?<>wezjeOU&GyLiSU}!O%9hUBX+@dUhl)xo_@J= z_bawGaKqBHLy6SQ`LqUco*y5-&eMp~6>l33Hq%k{kP~hUR^X1_KZU>mm~U5A_{SbD z2d+5h+4=EG{Mtsx0`Y9CZ8Rle-dBe2!Tel`U(m0>9?SYx?H*R5R3)DJ_>~}KI`roh z>4u5h=)!JbQM_^a_|-|hSwuWOAYPz5)O9I@2wo(eU8=w@iw3zrPVh#Qg)e|2n)qbCS{5oOw9m_qp zZE5wBG@w$W!KHfaQFkVOy;kVR-nqB=Yv$X#7xlt!jI-`!phP}?otE$+33bsgrQpzs z@HVz}fn{v;eEb@Nqsn%^wnZK#u@N@2McY*%+Mwr)Ds#22maom$_5-%CUL3?gY*5x_ zVeqwnHCDywALu^0Z*=}%aO(ScAGUCekpqc7ezm|8zE{>JtG|QyM9~I;j)iDbTTy@b zc{-^EoY=6j53se&L027Mt|l6O{_8jNuzWn(eh4X&-_Z3*02yNevRz;|)q1ZsR=uoV z&2?;?^>=zW&8NfJRX$$$`1O*2f7p;2vK$+(tgZ2DoUWaLUr*6@WRtxAQ^GVsFs1fs zFU7CDbVvHWly$Q9n*N7LApey{{E)8Yi7Mlld{qsk+IH1odhNTrxz_qJ{Q54QESG<2 z!;L?yI(ayL8^3;DDc9)`YledNVOj?4*n8ktl@1%KM6UM!EDE<2@oOH+4s+5XY-1Gu6)bN*Yk$nguUP_R^95APf6jSFEo9+E(iyP| z{MyeJK(;Gmzjhr_vg_LfAHU8aY-+^ALRe+C}|1mQp8C*84rD zX}Avkk*^>lX5CuGuZNv}>11nHh^5ZX0Qj=*vyCKs1%3^aQjSwYh^F4>BBaCmbsuaC z5Y5N0f2L<-$6I&y8y}%369X@=$v>(4jQaTs{JIh;2^n5&6k3WVZUR;JokNxQHBHZm zg)Zvnm(xW{%<%c$)p52hz2+JYLrH63LCY?rQDmh(^zkbyG6?i0(%lhY+=7{HO86Be zg;PZs1)+^L7yg?1__Y!BbMV8#zmT(gM53$yT9okX>XYDKVG2*;^uk5r+IVdbSGW*- z{Cbz(aj%!HTz_~%bvfbTId82V$u4p#@eB1EPNZ=Ba4pv#YT7kHJMiSv`uS^B4MK20 zraXOahxEllX z`PXAiPx7eAnPwuUc1{{+Oaw@IfIk1)&+k#Fj@ZPTmX}i7Ri)qK@Olkb0X=(i{Z^(ATcgxZ>uDx_Aw!pnR!~I_egepD6Fz>8mge-Zy-zFN$vecK~6heHpVq6reTU^ z;1_J>vYxufJlJCV@@=M%Upjs$(Vx8t8@8o(RPYNQzkq1#7q~SZwEbmw@Og2Den{;M zWIleOgb$gcXdESc4{%{XQv54DAR$!XSBj?OvITW&l?Pj4waNu?h7(xkU#Q=BGlyMg z!>29Lu9Cs`@oS8sP1a?D8>ko;eI0@Z1s}h3*pM=6PKq)Vp`a^HQzq9o1HX`=qrEBU z*9LJ1yYWdeqw7kegVEI0+Jk)SN=7Y6t=ptrEYm>z1sp%Nk#Fbbr1% zXH0N^c4mz-|6=?~{be5h{F`1^{k8D%D@RjK2WJDDfL};k3?aE$}zJ zEa+E+VQZZI`5E}NhGDB<#s5>teu#3c=~A{>#y{>k#TUisSP6 z7iYV=sLlm`Y1;LgF>MwTVLpB#Hh82hg-HADyi8L#)WBM*D+GM}f~m~3MLxc3dkk&9 zJj@tp>Wcb2d`QW5mE(sTszSBleoCvDi{UVSY<<51mvOgsU=kF9GiVbj@-NiSn4rwiWBNV_e0T&`VHW6rwiEX zslRCH;d~vdbA!9~)e8Jl$X^V$9Rf`Jl9rte4Zt3rlRA$!YC1g6!+{u&Yy9>+z3lu< zMzhvi#PQ6CyI^nH=U;2F7AdzT9vQ4Yh>+aI#o)`F|FRYcpMT9y9O_+PKnU&y`qu3~ z*Z|>0913n4tKA5dmi#T$A38X0?AFDNm(ss>4!fbaF#;tTaOXuT_!r9NyN0kjX8~j@ zB!ns0-v0_lR>rS4z$-C|cyp`z8Gvloq%lFy_7Au-^Itgah3s}@xj_Afi~gJU0>D7V>C z)~^E+=^?y_dPO9q+w^i9u7%*`(&v{^jo`pPL>${(7J#8fen%nVLFM}SA-bR*PX948sI#nXoZ<^s9h7WdF*FfuENK9}$d7`F%G>K?PV1>i_$ z0nnS}HL~{8e*MO5Adv4x-&wu3tHY`6ta7r*ePSu_>gzPs=fUg%4?= z{?N~VISs?2PaarRCLgq$#VW{z4Z`M##a1; zjYcor1q8~XSivP!huVx%{4mNEa$N>bqKL%}W%Ad;tAx#LHxniw1Ix*;kWeC zKKEq28XhCvX1^Ix3pO62SRjL^nS*!3~%5wp#` znfaIg7IFOsunEKIrqcwoe8`Ad=K?^h{EYeypm6)7k*60Kmq(3v6##lLdIV;%;oQQ77MO zj1PvvWvY1!`TQ4MG#QRpv9ko4{9}(`;)yYOMT*;S{O~+2I27YyJ zY)77`WKLTMH$_M|$rb$T;Xqr@0vyXlX??^if_hjY(2*4-{uKcKqPA|7b})WnO*t9# zu-tPg|DxFH+Swu`zb#KEty^%upcs@J4Dj@L_ixbhW$pcQn)m3k1cd@8O*G2;~T6pyP9z$2kbohe7aii=Ur7EbAeXI(4V~AUy6&OO(p*dpbkyd#z`l_w!;)4 zlU~8U5>?EHvabnt4$HKVk z!kLKX3jPJ{R%{0C?SYue6krd1{>85;o?ni+(WTuBXa~$iO?-hxIeQbPJ95m^9h_f= zU2S!vUlkvqt?_m@daLL(fglR|xu{>o`gtcb9_$o22sT-F&ZwnV)QTK{?3l3mTU@^Z z+d|lx+Q{;({FB~N0AxAdd2AHJg!LykIJ@D*uxV4 z3b4)mvRjkw&SAaU+!_E7JsJhaM;l7=drI~57LH@11f7kiuKU^~F|I+An~>7ym%+c3 z!G+>{$g4TZqmUSv>#di*e*@|+sg)3lv4Z0L8)LZhAuf?YErfq< zT&W!ooHzIDH#B|?2jA&#QrnMo6mUO48*NhyIDR%ac zMyp?@j&(zL16eY}4}0GezX~WZVj!D?T68TKGx1BJDmdnCW6#cChF=6t$wmND`L+#! zt(F$}et3dDYa7*ArSXfOcxf*P*Am|-1>tFLE%^Q@WRAG(kk5a`^j#M3iC^ey#t?)% zULXWL|8Qpf5LSJCc(*?Pg^rz?gNwgBJ6{n$EL#4S#tXDuhF`=%u%`&)0!Q=gA)j9^ zo&Q4q3o-gxwNW}r@Wa)N`!~3&w$PpDKDWTRb@<5){Nnkk#re3^Z7VTRxJ*FLPj@;0 zRYIZ}d8Zj+{#8-G!G3Qvj;(b-f$SfywWtpQ_SlU0Ve2^jv3cqN z9Py~_Nuw4*>Uf=>|KjssUE`sp#)lNg4-rWGAb{+^jQS0YU!&m%g0Gucl;QA$PY#!S zzaoBl_iuE)1=#AMku6=5J5R1!M*2pM+x+@N?H{(gp-Hh^ZjM6He$0g&Uj4=b&rcu^ zzuX%1+36QJEVd3rOb0)Eu!0Wjpri3i`~0)0*M)w~1MTA24#xh$O8n|_TPHu#C62D_ zbl2|s0SY;k*7r8=TgCHVM_>Ua)SG~){bxLo_OaW?#~aAA0(ww9l{C3&ATD8Qu{Wa6C*d-mb5RA9`G- zHe? zKs=U%ogcCf%C>&%0P7c@Ps#f6L&mRK{3_4y;f<~8qiTBq6@Yt6@k8cc$n`s*6~A(H zeTi2uq~&Z%{wMRwdaj@9Wa+d_%H%)Dc&1gKd(boSX1EpGF6wyflU-n z$XK?re*TeU8=+caf_5q=Yi+H!G9Z|{tq+&af30B?hnaVdJAGDE)evGk=|ogTol^aL zDlp&jsx`Xb6q6F&N%737Qnf-oM9oLJ)jUZs;r;y?lfWf3t}C0Qk7ga34d?}p&+&b zzZjQ8u$d9g%V3$9h&>|lq%wXe+g2uUw}!I~kc5EtwbZL3spzg+rvkquV)(l3khkAS zy%rxqRl|(>c{`Sh+L1BpogI-D2#Y{ci3Jhhp?mxb_iuz$bAg6ko#;UzQ7v;2X|JfC zKcCZ99YMkZ>e=oweqo+nsf8kbdHidUY|V0wqWVfIw9B|w9o>hEfOhp&SK?QM{cr}d zJ!JLPJA^=@ZH<-AfARereAK$e7DMJi`GwW!>XmY{x~`|@lK3Hp!*`Dy2t;J!Wc_0E z4*H0!8TI3b%)h{D>ohh|M25bJi_mzUTeIc+H&8z>9#44q1vqZE5p2ZmopP29j-TTB z(fH*e+M|q@+$rYm5d-@n25)ud1@f0iDX*oRzXxxG7*%~Zq>5#M0g zLX9gOHa7i_=Wm;P0hqN1MQimXT%4b z|CVTeuI5Y{(8c}P2>d!=X)HL7;_ax50+nhr+naN)v$`{7{5mIf{BXj20fs1)309-B zDDKo{!bPmq{L5W_OBksr^_n?L!N`o;{{z}I%f~P9FTUGH0Wa9L+~9@^#wH}f_(WJx zu8wnmiujdmK8KpEuuq-A0V*WJ+2!I{a$2?sKF~J9&Ajl$qdG+%ujOY{BA1d&aJUs?RwpRM_ z!@#r}Qe7jQ|B@qe(T32kjroLl?^x*1%lLI6w?nm`vXHA)8D~$zdH@6p^&1H=O~;#O z@Gr?XcR>eN+KT>SB|0C<^;$UF6{)~4vBSZ7vg?c75WXQ`&Ub)DXf^IyZX#AO|9 znUW7C*W}^Y>-ou*@oPD(M6(O_ha-4y16jnAjjYo0{xVm`nQfy5c-zxwPp z*IFWuyN^;!A;CpreQw(n#$Iv$i>u{vE7xoEu88M0g475KFyg-XJF`W5=2;{2CrLE->Hwi3U# zDQ+Xi9Nf|IDIDo)F%e8dn=*b?5vt{rh;KY)ACUM}gWx<`j$i2GS2yybozCqz|7D(2 z1H}0+Ty`9%%JW~ic7r;5+Ox(hbm7S3skOUd^mHJxeEur{*3aX@KD3^F74f=%>;o># zOV5AtwHwf#amu)k6P;BX$)wW}+`mzYUtHmjK95ju%f3Y7)7=dYZjnu3mHqrzzu3lY z?yeuAZM1JCFb3zBTXkr$opFT_f zB%w_e_{CvE;MZw$*m{ual!5%9#6%5N-oL@o!svZsy#CJAd_J~}`!{+c3MY*G{1@uy z1F<787bx41Ep81CGK)TyD(ExLN!APCm)a_0BaIvn-@>2({?)YAT0i6djV|${EO6&G z<3!5|^+2v}qtR(0fyD>G7I^w)UH#v4=NvxO+c%-k(c>zdLxCl{CJ3eYA@eUBTb=}3 zaUsWwmty+-R~m(gGw`dQbR{9;^Np>8m|i}xpmUKv|GFK}hOSDay%59ihtME5H#Q6S zv_<@4{-s7?Geh+*q0}`C{Hl`M$cbY=Tb^CrH}^5|jO=n__ruPEQcWU|7!=1;z7T`H zm-yERkHQV-zTwKL)tYv_s(^Zf7*~NbHu3#Y$E_6%ThsJ3@?YT;_}2+bpJ=AlK7L^z zvJIc0e>!rX3gY5Q>(1_G6)lwWU$Br4+GN2&c4Q*%%~=6oOK7jD0>8Lg9wx;_IVI?% z4fRxTmMpt;KLFKz9eM%&#Wn$ES*=xwZ=fQPVs<(IH7%HSwIdJ(%taZSSNHWpg5~%5 z*EF-pA>h|rG^7?eSf){^w zP(`^qd<2>*4_M%&3ox#-0>R($+B=!Hg1bbm-&}* z$Ljl1!BKGtr7!LBs!`$BA69#7YMBiI-1f6N_$ib?@Y$TJlHz&JC2>&B$FI-lhF|Si zw$n6nfhoydg`!PypRIEJ#)QEAy`2B*CkQUgJ30?^sJ#F1W%W}fQlU}MNxq0aWJAzx zWV6q|UIH`T(|~jR4xC#k2&ckz{%gklhgb_(fNME^2vaaBbpA`CDAdQVz0f$e60=(9 zfZU^bC4jy?1rM#-vvr*R`YFLH*kxSPe^$mB$j~3bEV9hM`URitXupexqtA$y@?VGz zQi}B=YTrv7Exf(gKv>o4uuxhD}(d=PDrXm*2X7;{94345$i(ytJgqqvBE z>q0~~h@vBAWNvUq{BRic*jR!X)Cz0i>6h-aYk2EkI=}1!a~a6ONw$ZC!hp##exVvu zE*b@XU8H}z2XOox9FL3fMFpWH!=1JF8xzQnF3g8c zHnWeUo)=smuSm~dB9b>zuzE6Sig8&75T64 zA=?$@T8M}K4u6Ye9SiD91*78p7i(qSh4~u69}bB3*40C}=hq$L=IJD1bV|))l8DoSeF7|3g{_BY-j?(disU_@Gs+L7I-f{W8@5ct(= z`e{$Uy!s8G)&0gsz}5}i@52~ahqs`O zsoOkV(l0=Obilu=aqWiWpd8`=2V_n{(TeeTzW?yB>TqJpa8abzk<;~uQwsHU<@yc2 z|Ioz|U>lh%(5~d#Gay+HyZUy)8Tl_gE?gT$2mfNtNsV7TE~B!3!-K5?WzrEl+Ane% z$S%!)F@A|5(Q8HJCvg8fY=X{z4SM>8zn_BHz`1~X1o)S_->u!n2MxUWnUVh*V4lmw zaOt`>u7~7{IMAPp`gyNxzHb7*TJniH8;y&F^ge#^3U)C6LMe_KFhl(eWO#!4sjS~1 z&9;8&Jg$tq1t7DFh4ja~wZQph>mYWNwHRvki0zCz3t;s43G}O!|5}ONi(^(6^Dh)4 zD*T-Y{e<`Pxc458AO1R@ACIjwUUYz8xljUy^cUUB@?SRoT1vL>Ra&aIp8=b&5ysQ6 z-|+HZi}2S{H2=bThJjzj8|lmVg>JxZq{L4le3Y!D*zEzUU%%nyzYz0e*XTh8UnIgX zF0F$;ez7IN*h4_HN2n!Y#nZQ!#_r=6-#?Ff?C)X;UE#K)>+| z8@XC@=k3M>4pr%K`S`_QLu9+g``$*W+d>s0W4ZTavI4)L-LS^}=`r(%1bUJINa-iQ zCTX+H^zvV5a{x2{s0~+tH`*NZYesTRF@?Su!2-;{t%vXqcj-$_xk6&~!rIDJq zUs2c*fX2<>U*{x8V1gF=eh>`iUxUlsKm#WvsUz9zfPjQnY3{E#hwY&EKs z5sTA>9JN)V7xBY=cA0;rXzKguv#IZo0@U(FzQaH?09j@J3pX@^f9Y#CQsLeFhro_> z^{l<8R6k#;KMdoFXFFh?|fb8P@ppE3Qd+V<24<)m$Rbyt6cA=P4L?jGIv3|Z- zf9L|y0MJa$c;Q|RU=p&yOX@f9D~2r&B=!~3xW5|)*r4zpFLN${Xye(S+w&-{>Aw()MMv>Uno0NT?HI6 z#gkze#+mhp9M<@hIOXYAk2n=zMMEKC5mOooh}MWF_?6#A3sIkcr4Ttq^>JM4J%Jqll`5$+x{dwJI7`^JRbH zCyZ&uHgnCn=IQPq0WVvx=K))GbPxX7Da6VN|c_xsP|cx6j~TyaYi`duc)U zhk-2LUCF;Z(>;sFCG{T5E4?Tc{7XZ?$Ard$6etXe(lf1$U&G#-mg*0~oc03kT8+c< z%)f9ke!TMhS9x3<7u5sgo6RO?@GtzU@PJpRhiPfX{5TpAEBKd&t;K1r?}Gp`B*MJ2 z{}uOd=sq*%Ldd~5yogiSy3b|)rI&ICyCN8S4#lMtbWmW^zE}NWl=15@6hy~l5I-#6 zzcB;0ItBjzm;a;!zqEO`Vby_Ot~Q`x9x2%88T`vL-Li;r z9+06;CI8~3WD3JcB`6v$Li93X75qy>Z3gz46Fu67vj9fs-@gI#&XA;Io-B+SFW2Hp z3Z0+9zZfqx&CvoAAN&D}OZZnCR~vHJkOcw}g9QM7o$&O_tKZOF)!>^#G2D>uJn(BU zkYpY7_*W6O0Q3l}b`ObdE-WO*UV&fSRX(SnJI}R+#f(@5|0=Ep^Dj<>F_7thRgNF# zO0I~9U*7!SmM{>68T<>jE{C)iuYlgh zsHcrw?PGz9KLj=kbeM@>h46V}zARq5@vh*%s1myrzg(_AESdM$bLXQl4wvw+E{;AG z?cr@&STu9@8_ULhG^R*m)Zq@TD8weAp(201E$%e>6=WrA|_Ye-Qb zMeC@VZd6*hlB!e{Klx(_#WqhRs=gBVtojpY?r>S?@Sc>u^^~L(HR3%1RUt_4NvY2X zGZL^=w_|5Yf-)WNi7K`EL{)chGIvXKZf!{rf#i{7z9BZ3O5+Lz{)j%~&W%^C@q``- ze?roo9XF*vE|PHnO$ty+upkis@0CsXFMgg%!NE)uf*($WP?hG;!ph>eo|09mz@K6^ zu2D5wkaN`)8v+SDnd|pi8Y$FP<4mE_0_HG-$@HF-#?@`cdqd7D%SPl4!b&YD{iHO9 ziYQJ9B9KnPg}AFOb(r20hx8BDG_KY-b5P(|5}vH;aPaI%lu5s1|Csq9okV}OMugen$pVS&oyA|)LgC2j#0xPT*Is+CQ4!Ys9U>L%k; z>iN`18|x~C-h22->03|TRCR0bXH$RDIQMB^xT5&PEw06lHyO98Q>p74>z?)nH0CE) zyR*G{zOnK>n-ltlCPF+A8r<2Qev!CLtG16u;?`&p=h z(3^lTx76lFS=AHxEd2@1b+zzE)BvGsjeb)6){WhiQjJcv6-aAAZ`c&Rg(s@i=Wf%% z3U*s|S_^vN9IPOol-pFv-lTJjH5YXWy~+4zLUX&TqzDNP3&JQZ zCd<>fVxy|SLb)b6r@?&67pi6xp4g`Up605JNB&d%k8huQAB6gnpr5Gnx4^?CD;Kt} z&Yote1>e$7ijAz*pP${RZfcsHVWH}#0EE=+#(q30zvsr}>|0iUCNNjBV6w0>X?sHH zTNWP%>vnO;1Qh%^;rOSYOmEHKCC4NA$e=(Rke& zEnK68KtDf`rAAf}0;+s0IC@199o$lz=ElU+_QL+#*B6D*`es|Y_3{4On@UZn>*Csr z|8nun#S51Uew*J%i2W?M!Fc1wXmpC@Y~$*Uq1Qs zZ#*>i`dJq4zJBbEvp3w9czVu(v5CL&+XO_9+--l@tT}Rt&`{%+8kr?r1{`|;?F)Vz z@g8?*F^@T#Cy@fKH+Av-q>X{6uek zZuP#|mkY%fmK!2%y5fN~mkH8?CIA{;ip#|(G63}BAItUC#?uz)Pg(HWn5jS`-9)q1 z<$~A74oH!rU34wgsLO=3{uId@FTrIWpdl3PyhPI(xLj#K+~RxC!8p*|@JecC3Z*vq z*5!Cwe4;$tD}kJs_#T+Qh(4m`{~OBx7g>vP2yIyJN4nlnx`)l zifxiJaQPCTA1uz{d*X7@6IR72=XcHuIiIETpqG+!c%Z_<&=1h@De zs-lH5lr$P}(D71Xu=K4}X6i^|{HDZcgLz|M_In7W^{Vj2D%U*CK2quD5?uBHx{S;6#y>RvNIpOM(ZzL_3$iq> z#3c|=0Sa#ETMWnbC0w2%tSmM8Uy92!;kX3Q|Jnc6<#1eja=~vi1DAb()`H(ga9LAS z*4mc~ejBQ!f;1C^{6_uM#7>({mkXsfC4%A^!r$^9RorCS@6+|AN;n9AMf|t?wjIcu zv-o3qZsUx9?!OTGmVeOZx4+46Q`?U}7_d(+u*<^M4)2NIX6CrSz{-~1`xJNV@@XkO zDZR(0E8>9>`YW2}UM38Bh;hk#Mk*(K-eB=#Ge*@rLuZSg1UD?rGXyML#lP{}lpPpu zKf?d7^b>MTrzHQ+{=0nn?`RPY|Bhe&d;O~G;>C+Dzx;B+|K#Pr@}K_74Q z{J%<`&#so3SN}K*P=cU3Iyn-6b95bArFkYxCRuyXf*v<5~O`Rp{kq z^HW~a3SBNvod2ZQXr?Zg|D~4R*X8#z1^?gb`z1^C9{&?%{Q=%4aq9l~d;I$SNAJ(T z`!n$V47@)B@6W*dGw}Wl{D+?b?JvP$g1;pH!%xQh6Y%~Fygvi)&%paL@cs>vOD literal 42175 zcmeIb4Rl<^l`guw&XIhiZn=+Si&4NhM=jyBotE5|jB$)Cm1V;y;0c;Q63@J>c@6R8 zdU8D(F_ZNk$<3W9{TRzX#0@wTCt=Mr#<`fTBUFY=aZky!IUGKeh?^=VhR$QDeovvL~dw=`edsm67qIvIsM4{Vh&gc97 z!|ng;hR?V3ZM^++f4aW)i=SIhcTjcPU$rOyw>y_4lk_CNlQ=&b67xDAGZ+`0YNm3yqnw1Qt_+Kj-azdoKT9V}Qf0O@tRg&<$_}`%< zN$REM&rzMtkAKULsq_cGyHA??gbdyDxBZ{eABf-fv3~CLsry}h%>Rx)@^`$*)Sq3j zDMbxbNg<^hro!ZHr2j~UOWCe5EtgwyLB5x6pNbwcR>+I0D_7HJbacOD@5@Ba8F}jC z7b*L++V8~1!=n^ehg55B_+WQudPjEA_pHZ7&P;Nj;fe{`PmUW-4p?fGTHUIoh{!>T zDQk&18Pe}@YxmM)b_{()oDMpzW5e0H1Z|^5%F2qond0|Q{b_nvjk?inI8N_Ur(2Vs zlMv6-s7hpwNmIWk7kZp9R5yC2X@)MKaZ8_Zp5CFZbTn_wKveuldoO;S-(yVE^VF%L z**OvMyxpcE{2qt#8Mp)m_!Iok8`HOx0at+N8MUuwL-o}wNkc%J}OL3mMWHcW>&rgQ%WU?~F-!)Qkntnar z=E?P@s~e})Qx4?XXT0XES5NH&^awS&+xo;YI$$-r;XZL<_dmoNmDML+_U758zoRGB zHG0hS1J&qY@GrVMvP~K5DqqFE zTBf2S81p%5Pe&bNnqCaGk0dh2Iq$oAC}+;lqZCy}j%KVU{04P$<7KEYek^-EFO!^| zHkx%K%8jFTn~bE5Bi?r@b3JX5t-aRH*fsPjwaRc>G}9IplU7>f$maJngiNtTez7;c zQ&i9vYF%}6y1Ti%pBx!ai)XyGu*?eTmx*3$ZgmCSO9>gag-N#q)R-H;YqUP1j5iFu?z^r&MQ2rKs^*ySHl3xYjP49i(b>>S8NJsyRoSP< zQ!yovl9r-^jzT~vTHczVB+K=Tw_eNblQf*!9glCVZWF`QN?|2(LOC(0n|RTa-KrQ< zo2FmZlPvOZZk7wG`uQ=_Yvo3~;k04iP9~xLV9(>w(&eN~Boxzhf!fYgITz1-e zamQ(tpFHR3SKN$a)nNfLVi9enR<$jMWg7OLJm;-NykafwS5D4aE~;se5^mTLFVePj z%!PhESJ1BpdPFu(T3-^&oz2wZhEw7M8cpNa&(r>Utobkn`!k8@@O*k!t;|I?7#Eyp zGfQR?cNwnG9F5d0!oP?D);^~*HxIn*A?I|o%8@&X37};QS zIxih=&qrnq^44plb{Bmg^KA5rq{Mi}s#9Vf{WofIF`h-f9is>6acZQnE3Tl;wAigm zi3l2vN6-f*gU7ReF4PLvBV2{;H<;G4N9ZAPSoSJ!-AC$S$COrcOKfvH-Kva{fP(DL zC9+}vyA(~+yY>^FO<=iZXTq?eEMOK3m*npX+2`p3b*Xtdqk~?ux(-Kk#tBSRJNI$d z=sx}CU9bv^)vlE25ZhqG!x{00{1GjKg%mky^LtdqMmmJ;H=Gs@On0k$RYshqZEV%i z$6E`FEZT=Y*mVxpbR)0n1l>=~F081pUu13M9Yw>ct~mt7=e0ObFH!qp9*@z>-<7#~ zhE7lyEtbX^n8B4U#;on33wtUOF-#`OU`!)@#2Xoe4q7=V8*~gswD3Pm5io@8nTE9y1%d-jN>c`2hgDty_={uf- zzY!_LFTGb`;(iKM>lB~RKz0d!v8i0a7IH7->X4C9<20VM$y?HMO7QED2U|ax4e)1id?$$mlJrMK>@XOTt6}G#_=s`;K00PEnpPi9m#sc0x z8oy>}1taWk8dsh11jcO6q_T7IB)~6i-g(SFr}t!6FSh8Ec{Zu-d=bAG0?;S)c7q0v zNhsslOiVIi0l) z4eL|#n>JwmzHXL%5nGZ1e$@wK{snExjP^!$YLIG^3E-D!pW~rD{+NwZ_L~Zuc9$__ zds39+m-aw@+6+Tvw{ACbs(@eQ@Epb~7{35pnYv@#*lC>DSal3pL+m$%2p zb;GitXxpHJMf}Qy=D{D+`}qVK2{qgcMf(OVbcg$5K7Ne=zur+Rp@YVcxsi(W7{|?L zbNkEi3s~UU78rc&LnCcWaN{@@&c`qE?P2(=aY0^G9|jJFI_aY9$|YvzxVE;LBeni` z?i4&84D2@JHhRQqaiL$Y74eHds_LuLeJGq8z;D@q&V5{OejN4>J=tH9`_l;uJ9TRm zKY{GyCHRFg*JO>?>0JqfwjB7ywhoH_EAP7$6>NerM(n=jq85lK+OsCWQXFlU~DSj7yJC$Vgn0k(O6Zs zd!yV&@N&_|$1gKdkZVi~(ujn9O#sNs@T+Dc&pcc8(}3d@swQJ>1b$u1PYU=|6QETd zgTIb;lc$r>M?I}1ONpC+ZoAIkPrD5w))#`#SO=0lZ zV;SLi^X&1jR`zluP_$MFTQ`O=!`3+>=J|&%d3&yX< zpzo1yW0~Gn9er51Q}leg&5dNjb_xGtdNK)mav`k=3j_3%?&2I+K#KLk$FGX$<9N@E z*=b|Bd7?V;9>XrhuSD|ZB*w!A1ml4(fz`40h&FF6z`uAcM3}0*MTsXN`~AGfc8lc& z|B(4tixXZa=F`KO#S8&&6N7>^EiCUn9{=jFqHRXR`Zaa9pj{L6Pfn+sa0crH51FKg zUle6JdC^%phGjZ0{*@V7E<9P8;yylp^+avR^=1as0-FiBz%(X z2VXv?+7CuDb6%wvGi@rGE8$-S{9-r$32QO10N`KFzqDN)yX9Wcu6IM7t0J0zi8cvz zDBu^uG}tzM;a!Gq0KddFqF?Ur4ex~R6!0sAKE0ZnaU(ugw9pnA<35+-S6Zy`97UUX z96;ex{OSw=tsIQGgCc4U7&{n8Ier}qb;?^Nu>|i@*B7GS+V=LI_x7T3e6S3^y5x?0 zxLJcj7hz%9Y*7@ z>^^=G%sXUHB^X6*~RL)oGyggRU{v#HAx;%zsdNOU^B=dwOEC7FmVqH#BK7zDZ?*i zjdpV%?JQxl5u7C+xa9X(rd~?2lAg_EwKCCXH}?@C?n30@m&C41fL3Tzz#9qs$2O?I zzchY@JbP&V4Mp8>9)YQq6rC)?uXuZYo@=y8@MR}D)1-M^E;n85MF3myCE!A^qv)bhieI6a8z1RTIDiQQkbRmEPhjjW!LN4_ zXS4PUwg|vYE_%>##kd3eoCNM@|A*s;Fc&VC;7wkOWOTwfrOxt`viRZ8sBL`h2-f{q z9?<915uus*q^DoZzreQgR&#ZNFdho;8kkbPzfp`IGNT`}2y6NfB~*Csz%vK{YPl|n zA6l;?{J2@QGy?)&mUlB2l*JD*p0P-6nCNG)hfx!9xs+wO3h_gqe??arCnV&mg@9*j z&)ZtAQv6Cnij2`p&r`=iY=CL&s3%ux{Lp&P4X5hnA!N})mK>PCTIfJx8Gd1emiQBl zd2!O}5y#QcV=m)gkn}?X-MpqwoWx54_{D2lhF?IdDdRs4JV%`wrd{f5xluL_#rPqc zI9UFxG7cIJARy<=z)?gah#yYTFKM(lx^oWfs<&QaAgX2fh4`UnTRYi4d%E*;;EuFlMG*xq)$a^b-6+ zoGleSq2q_08(`kg(0jSd`B%HdM_Mi!?E{c85yO)pe#rbQM(~j=#xDo)Lx+YHo?ODe z^avTh5;tj^&>#25to6dV1;bX;a5&Q)hq(ZL zO^LXI-C)C5#=o3QI5D8@h6^7_R1TWhV-EWCvZ?d%t5Jp5i}?!sIcN25<&CZVu@e4u zF|#BS-4LGdJgX4*yUVDALEFcH#MesjOFrR7_ZX*L*v!Ka8Kj0cTc(VEjkjl_=Zy=b z{v6?9Rx=c3bw>-ifah|fPshz)*8hcOqs)(rqwJb}iQ^RLDs_>kvW2VHAYgPrbE z#=o$FQSlP=3;w(%YUnYkWiR1h7kO6G#(U8Bt`m{#j23*?D396adOrV3Gi)g>&Q9ZX z1tP|MeE!ApLzt9Iw0E_}FE^4my6^=2D|ZS1%3{4Vez`E_Ew+wFWiH`gR`_0QEhb`B zY2CoCLmv;nB0heFlQ(%jf0YJk%J7={@eRhWi`1zgSBGALd(vSBXl>aF{Of8xX22Fg zofy3PwMlmDJgO1wy_?1eS3#WPE{GU-3cYt3eu0+e zpr4spuJ8BqDq4_=si%_;qUFRoF~~vzkqx4oty{ zYP(wCU*_k5RtZ^?7G~vlhm*a~uPL(-KP=#vX@3)@yFsVMY8R}FqYu{9v*88*utM~s zPu1K7H!#@s#(`h7uYd(SJ%}IPA&x2dy~E%aCpbM8iC zWeUo8D)XK*TXMtGuZGauKnM7}#|P)q3$%nCP6mZ?{Q8;0X|I~^bvuOkA;C>OsSeVj z68!3^7*jlEt8PH2>T(ui?mlXk0J0+m{-r~vvg$tI`!^-@RL{?lc9H}9Iwd#_7ugv$ z&7*df!-hPbWjtmdzcz&SC}@g|?&5Tw>Rf}5xX%ji^E{<^evtptuoWE&JL;Hhv%LAa z48M?89)M+AL3a;ar)n}tS8+xRPXhef$oiE+4!VZ!x1C9CHy$I{@F4${2`xpm5WBZm z)Y8|*M^sf`)7!YDot8#eu{nxo9!}^`Y(BR7SIdIoR4x zHH&S+ySBD1CGo?5RV#DgUn}T^fiBQD*u#s?b!-m<{MxA@spb!>ZGxihv=B&?kJyc4 z;X8}?^|$m7w0I0`YaIP6~c4*qHu;QJUYVtZH;Km4|5A$QY3)#_*)?o|;K z@XIoh&VyXx^a4d$Y-~?1inCk+etn64#4bdyk)av*{os{M9<^NM_{Aga^=Owj=KsV` zrak@Q{MTuDofRDie(6M59=vh_NhNT!d@+95FJb2u(q0H8=772J&@cuc-c_3a%A}gp z=(CmsZah>AMh`^HF5=f-QZn}3w(T0SHFn2o*lHEw^nj0FPW=SECYRaa?Z!rLvmFe_ z2Znp$0Ds+Zi~I`_=?rjLoA<-fA0ljsg>w)UDTyB*w^vYPQ+ThxX^m+ln%itxNZWLH zZ;V&8I9uW6YHkHzB4^wO{Q@5(D1Mt~>)eV@sQV~RaE-RJT)EY|x_Q@S+_;0Cp#4MC zZ-~vx$ywv#?-;gD8h2qoGncV86yk>)P`~j*T0WF;jO`3tW6`F%e%G$W2tCwMrB zA3ng|#aL8^zUsLdSTCjXU%xD^-#DIXTNhQ@UD2C1LCEmtA|u8kZ@svFW3Ud|)GOwh zkC+Tw0J8ld9h0f^_B_{be0{hv7akY$J*c&GLyx(S93BtXZw#v4?#&;9Z>-nU!NTn~ z^?N?)=@-{;9H+K>qw5XC4`CCc@UdZ|P;Zk7f5ls`9{MTesWZY9#)qw+`3={&&-*UR z1}Z-R->_d zm|3J-%zurEciqmu#p~G4^H6_m{6d|T82ruvzbak<+X7xpYW@YVn*{OEg&dQH&%YXw z|6=@#E8v%x2&<0iC*}MLHbGih43#l8(2h(fmF{h1Y;m`B5x26{Dp?A^8hb=U0;MXr%K*Vyr_2T*sq%vu(^5TaK>l|Oz zoVvij%oU_*S4MA%R)#GQF;h35Cp6dLG6`!E z@dWLLeF=M&Nlmyo@*Db>)DAof7tNJsQ9-ellZB7WkKkU*TSFJiKjyUl?I2emwx#x@Iz*7xOH9 zB(Lfn2z*GNf2HP*!6k;N{5#KPO4!T^PtiKXF>ftK>W2aKK$+|y)SNmb+}-m$cqGCG zOYrNQ>d2u)Z7)9hYYA~CygpfIoW`b*4OTLYtXUp>2Sz5hof}{-j0Hjb@bfe*kv-Xm zcofG5VFvv|j%+c0xL$qRf$8>QjIBM@%)hYf7~>qz9v1Lxp}(eh4+U%LEd^24dbnyj zug7wAx=?(sOMzTja0RW)nX6v`Y<0P_dp$cZG1T(~!&t;G32`zJ7ie{!+6j$oX+pL0 zdIk7(T0QBuW~^ax(moi9IcPM~cl&kxFz3y)&VNaS2uL6(25I3Rn(Eu09~JPgy*w={ zt_E_>K}-9G&B(?98O!Q7E>eezJZ-!w&-d&F0J3-5bm&X60p@}Mu)>E z>2+@{O7IJ4KFskAz}5;@JqPpSzsF(S@#2Rf6TU|{L&K)i6RyJ&3_5x|KL67BFX3dv z9n}#Shl~oxs}t3J46P)7xL-ZETj!ECryDcj4Xj_luk!dI>JL>k310b>opiAe(Exsx z^Dp4CLbb;EH5csas3{c6u=hBqSRmt z8dv0BTz^O(1KUC*5@j3xYct`6^rF0ke*psi7A7vtu+_n~Zqf&`BLA9#RUh4eZG1|> zymNz}csA49W4iv3VkpTFr%agT6dF?n`&`U_QGW=ODvgxHJ)y;`=w&pv*q#k1$#cl{ zha6F~dXW~~v_|JI{!eC_Qey#k@#+r|KaB7mL;Z%67&hL5Q_S_LMf^G?&NBbnY5ZIu zkl1_6PU9Dd1L!dq_*Vs>ErTcDG9IR*IiyjJ8P|b`={`aH@MPwoY^Cs+Xq3ZlY-i7R zM3d_mA{Ofp>H9Vi_BJs@{nFE~NHFF;9y8*H$o*-gX4r~J0tq#@442_ov3_HZz=ZcE zK*Z?ifF4gt{7^%iZH$2ecgP*UFL4$I4N6oJKh)X4aac$+?8FcgRXEaE+yZ`80Ig_u z8n88uMoj6H@zzXh8UI=fZskm4=3nGJmaAsJcTnj)7Q_$dBb(fK1TgV0wApE#tnNb} zAHM?r)kXirY-^qIqIjx5o14AdxKc2FeYnWKuHGwjDk06Teyuj1=L&Ydgnx0^(1)$R znSvQSM?QY}{A;-Oaqrs!Tl7e%Wg^^HcZ~6i_3NbP9}+!K(#QMgSRn>d5PZ5Ln* zw2St4YuN+-^*G`iTDvFcqS<+PM|RG;L+~zK*7pMcI!)&!S5dBpT--Tnj1T=Nr27Q; zb)Md)cAZjJfUR-ZmgV%)NO}FnSLsoyt@;~Cmm|EEMgGg+2vr&XLZ2hy2gPaF!`8$2 zC{AI&PeH#T;Nf7V;wvjeYTuQrdNPa*$R zI}KcJQ;S)ycM%_(&H2g+^{^Ys6zew*0&1UB(N~P~HtJHIs`7C8dUtVI{l>45|Kd0s zom+$V92lH}&Ez_e68@#O9ga=*{4Ks~e>(X?dR&|a!nT#wZ`>3b9*Qkpo)+*AWn!|f z1${WOgz% zffan%EwA7B8|qqPOh?(x(A0hg4d7RGkt*e2vY+5@P4*hT%}m&M!EZ#~GIF~F}0z+Yx8p7rvuj9;j2 z9*V86E~(!brsJWu%;G&h|EkFt@2VFifVH%K!$#PUqGN*@$~%Z}3>mrDrTMQ@^lE5S z&i)p7%Db7i)NIte0l(T9$cpj9HQ--;;7xnzP|oSIvN4_i;wMG?5?|lact`bQC3BEP z>#FY+PeXTN%(jC3R|Rd7jei)wZT1kjjK+ee0SXyZ)ED&YV2z~7P?Bb+;P%X@M`?D3 z4K)xgQHoy?gx6AF{QxNm-#86Gga^Wk7U0*J?pGzs{6KS{dMjm%#%0we@ubqzFRq^- zP)-IZNC(sv@fIb<1;+v6ZgsJKLv0E*$@t3yK4-KUe5J0<5FsDGzJs`Nle8w8e*v zVcUHAjar$S{bl1gJ*$>DAYzLIt10T|w+jqaF1{Q3t^s<~(1nOpEW_`*rKcS-(hM!bZWYfCQtI70vf*>VT<8&p!iL3gd$PEB^T zi|;z57No)p#0Wjw;}C*`LH$OgaSLK-2*K?|pVmn%Wiw?N3lw&*OC6H=S)pX@r zIKa&k7&oN*#7pWo);k%z%R@hSXS&qGPGUg`uX#r>v%_TdT6CYA;X*u#Oc^`aiEJLU&ea<3)ZWYf4#v&&4tg4E{-1}h+(X^kzX#!e|hzX@En$kh=o{_ zg018D2KHgHG=6xh@=K(!VWL3YoUFX_K^bq^DpO7I#~+?rIlqq1x65McZ^K|DuO^gkQm!5%Wx8sGZJ`H|8RKVTUCM$G%`& zDB*6)R#(%YCwn3P#r&(cb2OT1Li{k)H4NQZPR}s^a!Ttr&SR*|zpzC;{1P*gpBO>@ z3wBj_$B`^eSklHdv7;HCClaRCW$-WIN5g*38*+VkS9Jxw>Mlb3u-XaoUtY8@4QMOG z4;^S6#~3RM`qfOE6u68n#^&y$jlEc0B#bD|Peh1S&_DmxMPcwS9&-jmbu7RBu+`JA zU00tmp?a&Be?bSk`l443zMXm9<5mHFX$WBcg)%=vBCOZoL>S}>;)jL$!<#pW#_sK; z@ypTpRT@8hMXxD5{JG{PwsnYaSg4QUF$ejtJ@f;)c-_sD)k`r6i6d6Jy4l&LF00=_ z*&)nDGf#LbVH+muWJV=`tek(P5H=iR+tM?OF;^S>68@E?^Af(X+r;N?I_=qNkn1;e zelXx)3#~_oTN$mGe=SD+M)mddV_V}_kpKF)(tC`=5xBb>-Rk9{TMbBUHv;|zn81F% zxsUl5_Mx?2%#Hs&Ep)F6&VP+qS2Aol;U41|@r(j~^=SSDMpnwdIK<7GGR@-f_B%Zb zl`8zCgnzxL+B2xuI7btz3uZ9P2f?&G4Dw&QXj7|LZ|uky z@?Ttk=w~OVIgV{pBDP&1LF~d}m*l_Dho4N-A=#?rt^vo{Qhxr+w4i#9gNV!+jhfi2 zbdy4&Ipdb|FF)I*4Zez~)y?9lSG01^!j>7QGZ}cN61C zcfO~(Qks8hzML=euQ%u?{Y^Q94N*u>tr_qyT~VJ4pBZpG+cI2niU#e>7~+Q$uyw$= zAeOr^O~oW;MX4YE-e|~z7xNG)YeB7gZE6D4%?{liW=xeQy!kf zY4eX%f)~XfPhJH0^$9R~Ctt;K^$(*@AXl`1`(qw90{l8*f_C)+iBNW^bO_GR z6FpGEzfcCyen%vYx=Pe|NN|||3j+Ry{FfQC;hiG?)vxg@#|DJ!H;ViV3=v~ad-<>rG|WIW*_|Ekkr!*3Z= z>a63-h0P|Tb~uP1J}42*up&b|EeiZAm>*T-U(4+$s0sWFw(be|QM$o}^rigk7PUW> zNfpe6st)#8vdF(|^Wl(VkMlN$W2UTNKmTQse_cs=wIr4B&wpiR`;A=me35@;u#`_o ztung!Ezq5Z2Lt>%4)|MAFoOUx?;schnL&m8SDNl_WIl-T{Gj$)1z`2{>&_zo%F3Mx z2m3=sFdqlOLORBnEb=ecNt3fG?up!-E$SD7sGfd})c4aQs*58_q2^~r+dxs@^Tqo4 zUm+LyL~r!q;AsTi{=~v;j{0LgA($g|ciBQ`#PS=f7eK zvH+qH%pVyb$l$a>gBIREt)_IxDY1i2(?l9lnxf$_sJnRvjU0Kb|qNLa}1 zobzldKMz~?RVdL)x%fk+{OcSgRV3Mzkl*gb22jRA%F9tVk}kv#-B1GlM$^tnexTKU zY*M(;cRmQVn;UJOuRdJ?3Y>I?dO8MboGgn&!URA&4q~2u{4gJyPfy)N_`CdxKs1IVL;M3}5e3BO)K(cjSzR1@E}0sgh+nhXZd`7aDUUeGVD-`JOe$oO@* z6po95^Q@kvE4M=8oG4 zU$4SGG-;ngp=MGj*p_CuIxcpZNghuI@eQv0#BS04h8xXdV_&2;H?e2%qnm#?gJujn=4) zv3J2`q8w)vr*=IiMqD@^LHrP*Giw1tG1}Enq|j%2i0coB(Z`GTB7TVd>=5`D)>I`Z zp6uqs>3EMLAgcZ@eg5lVh~v566k(|4gc0mmP``orA$BxF5*pxNdE8y|q(Gc4SBMq3 z6>sxL)zQPo6Bh0jQE)TPtBJ<;@yPg`ytiKd`Q--G{Db2jZasjq`S&tg(;s~S=MeMW zJahaIL&Z7YJGRqcD50v8VnPDHj$n1(^7IS%Wj&_8D6X=^-fran(g1N>#6fY)Pj?}H zSbJI_)+D1D)Y7~Z>bzIixQ;sjp#H&P{hd{6FLc zUcpV?{G|0n9mbQ$uG0A7WA1D>JRI8YK1tf&I8R#$2Rbs{Z+rNatGJP}R$}000xjgY zXgC==&2?WcfUL;BLXQl$nA;giKbIv<&FkgP9G=6x_0Xpm%d>yiTwX)9hvXe7?CGo@k zR^u3K-Di0`V__)%z-FuE<@lGxTb_Qo^eyLM)yS}=X;+pxV`#I20r?87sNT;#bH^Kj z)Kj?I2mDJUGPqm?3E4@=UQ)lYi_Q(VW!HWW-_A<(Vdh`Y*_|r#y<+?j{LAVPi%rT& z_*&w_?5pQZ%tlH4@Ra&d8t3C4os&>H^pRdYCK#8YxktP`rp!Ne`dw{}ukk4rbonSP zV@1mq;)l(&g-e67cQXh1qP&WMOjm29MUek$X8whv&9gc0uu+C>f&3RW_r@EF`L7g> ztE_{VgF`bzoeqY21W(!=80z5ujWgoQh{Z)802Z9(vct6T3;HELLBgm~<5xokM%b^V z0Bk7^1ci9aopLSuc>1**=TmgmtAZ48pA=|U&PL@PAung7x-8AchM*AVRvj5uzL)$>q4?Z{bBvjF+z;TJDc3bW1bR6Wv5J(#1HqQ_`ME#+YPjy<`2Mo-QN2PNnE|Mb5PY^$>Rx4zyg7sU$_Ruqo z&!K@QLHzI!Xuq4tV|!FO9HWK}=evfm$CeHR@k5&)v09GwtQ)YMN4zlj35CGj1a8v` z>No1-JL!&Ge0lf^dS_@QhYc?b{IkNn85;&)17hIEy81!3>f<{`A&xfhxYQzp#_=6k z+{%>hRB?`8xUVaZ^I!7`)G)Ul^#G1O2zIuJU-#1H20+p$g`N6m+z(I#{SsVH67MbY zuLI8Jx|TIzS-F%pljBzRL9We+y<53%)6*{p^@oEFTz#|BgvdCM$g*=q2W-og-ku++ z-%ZcEQ1d-=77*0ot=U=drsowZ37-tNdb-TFwfvg5p9=rfM2+HjO_q(GD32e$C12Xz zKDs0S)EUUG3lTm3wK0#2Wi(dyo2%#_hZaqTvje9ThsB^)UOdX$H{k1+s)*Az86#la zDJtocaC=l;&a-ulUk{n_5c?!-Gud2lP)^HrB&4UA&woK2ijpy9;l2Vl!E)(=!8jPb z+$nSQ37oLz81=1u|AwDW(G6LwpP!*IE;~G8B-Mn|o{NBXA)wu!)2OF)P}#qtZEoz0 zwGUSrY_s8q?-aPujPqY3#rpYi$yo;M%(xuJdRetVTBQ)nwTt<$5#ll==vT8qO5L^U zU=;Dh#U)xkeo;M-XNl5jf@cY^b*4{e$PIG=y`Td?k(ciLbnq_Oh#-F0 zBL=48wnf!&V#aZZ+ZF^|PK;r7eEdp?!g9s#H*lgk z;9o9sB`ZBY>I`h%tbPNC*3$_CF<8pK>_kNIJD=}=*Y~SPPix98iV4B3OFD3b}d%(X~WVwYFO6wRV;RnuN$na$8{ZX&_(`!Pl z7`CpFXNNJK2MRuIJfztck8rB;EIq+t!>Kq28>8PG4C04bb6mh~s3wOdM7vdVlHDSH zS*-7A3#^p}Xc-R1ieJ*BW~;h+j|lLqN2rsTVYuiKfnaqWHv9!ROOdybNe}R=gEnJ+ z8u-48?R1SDmg0wWfLdzN2=MDldP?ofti3)w-$mI*P7|>+3P855cz$_1EM$k8b-;Mt z!(3gkXLg2e`7Z_hk`>44CHy+lDD%;1cVJuIB3#7bL|!+7{1<8q<%2HQN6GCr)JlpI zbRg8?TD^t*mrV!74r=1kjt;R|;;{27aY7!ztsRK!yX3Ha-hlXq@rv`Z)rPc}8iJ`z z;-mdY0wT~c?qSNo`eC3Iw%?iXN@6!2e)zu<2W%tvl-3`9I`f zBs>s5ehB_0UK-z@+?YlDa3Ztc<@Itdx?Rb&IG4S~$FGevC>;W9C1{895QX#Nv~{pQ zaS|sog8Wz9hUu18wmV@f0+6xmcu)fD(5HZ3Tv3lcxPPM#RN959)c6$w|MKxmA$|z+ zB_kZq^wXxmT(c7gT7&zeaDExp*D{(1wE6hu6EQcyuh0cLuaNf2 zg?rOT`ur5}LmX8*V&(5!M27~Jok0F;5j|ym@B+H5y8|4NHWDC5R7JCxKu4 zZ8(Zci}|m;p@SNZ7c4@saluw=6!-VGo3U#p6$ZPXv;QOIF=Ys&FMoPm;wr15d&zm}>MeYe@@#_trbsKMUG=kO%JufL-MIREt_jvxLvIJz=6ad|AlZ3C3>%% zizlEkoGiPW_Q?&%f0fp6@R<4hvN=vqI5l7oHWwmBi}|k)ft65K3YPx_2Y&k&r;Rn% zS$CPd<$*aR{7WNR+L%Yr(silX`PE$f218VgANuznZiAly$cY_Hex_GHkFcR#$K@z7dF6fLl(UaSH+;xS&u`K3!*pi~k-(48 z(a^HqucdG0yAAJ?(e$ih{tF}2VZ*8PS*Npaq2^x#_*GKBk)at!XE1U9M&GiAgkD^N zM_rQtiUa&K?fQ_s+ib!n(SfNL6R~PfFZljmfGA9NWrPOY1^I=^?ic!DK=1^8dU?x>`6%RO|mwEl1} zhX^L4j|}n>JfW~&xTiucQ_*7ntDfVB?Nf=f9)1DH=3JzUE*$%k{1@W$h-X@VCFZT! zWdGw%fYc=U(cMMfJQE>)NKHq2`YM^4HL8Vu1Fs^+*d(j^goOFP58offb>>#G@v)e;3COp;m4*8@k5D zdbzW8EDph$Ph@BL_!WX@d;v*-NOsnF`Uzbt6M5qR-UCvTEuR0{i1F-ptFz*W>UW#o ztl@(yk5T7+0I8Dr;iIT90mi*S-yU8tX5DwA=4cN0Dd1OxkZN(OlQ(jRz)>Hv5l968 zqN|d1zJJL4>royLAQHzsm#Ih=$?=OJ?6G_?|HWfo>Eb?EUACbX^O59>;$sxee`LB+ z?|B#TUPzI`>U6^2U|b$tO1MZNcMC4c_WVPRA3mvCiFt|`4mIWYUK+X8LUJ6K>&>%Y ze<(o}E%?SzD{;3;+hV*A2TpqW<==lusH|)jOuNGORsyM=MebGEfkONc_ysySo_Ne? z?M9&jE_)elga@*M=Q-eCkMo!n$KFj47XBzNE-Dn9zg6U4dx2J{pWkQ13I4px7hgEK z;^jb*f9d<9vetIkgn?sCE|#DL`{#;rGy?vG{1?TN!`pYGkLw(UFY$TFs{{Qi#t-Qc z)s(Vkx>wBI)Zdu9sweg*J&&ia$pZ@l{>5eUoJbNV9HHcR)Cu3fPuyFQrSZde=m!#I zq9^16gBLo}3(JgWV@=$ZN%mYEXW`MRK#G z{_rho*s z@dL&J2`efgU@3mVEQjwMFhQwcsP#osqIDfuNf~}Uv<8d={42%A+$(0d9UxVPUyZ%t z+r+iBNhXm0+8WuzwGi>0W%#9do^N#k6mV{te`!Qh;edYuzt%jFitcLC7t#05IzISz z<~JiCV#f;ni{ppyaePBIEhU6CWMtAfV;&Y!nb>LESm0kAHbh31W0g3p!Gt$QTWJ9y zK3`J50kk4SmMTesTvs!G>G+{7x&!>8LjrUXuA}J3ebm+W)s2O=fQ;cbl2ZJ_JSV() z9+j<#c{)dHG0zVe8$5s2<6oUZ@39%DL)N65rnc%kI=*ED{0sPnrA(+nhsLc@!uhzE ziHdp{k)|Mi$S2tsljQf1C=1|j4;8(!ESicZITnW_}oMdAzX2VBMCeXRyps6`02%uD{L8!l(8>2Kh3XBu=+g{y@eo!S zw#DaP-u;JdZzT>H7v%FQ>$YYQcK%lybzvp^?*e|A@0w4r1vo7rpj&f5{G9$5&WJIP zB^fKd`=dIKfX8(Z7r#X&2f3oYYXjrO8r{dg|L|>G+lwUEF*x=+1>dLFO3YKF&F@ha z_=xaPlKXIDyEp>2g+lr={Nh{L<81CYY#1kqJZK{Mk}c?$fB&Hi*z)d=(*6$)f=P^- z3~$}}{wTi;+k8jB400k2mY*=@68yU0bfMUGP{$8tY-RjDBD?M4b+i(DCUgySjG6^UV7AO9jM}X6ykN= zBYTAt<8y628Bu~?7i5>6_+fZy_gL?~+aeZ%RUjH}VppgHzxZ4mjtHG%DxHFz|7D#% z=gSvyq2M!Dzx*gA6z+5Qh!{@AZil&8 z2;vOZ|DAIDO313#E5lm{WhN6B-7& zPhax)Q&>{Ihx2~QsGjAq1R4nZmgK)sKM&YSG^0G`I5$$;mZN^7O|31xKZ?00p~N~D zo2D-NmhDCkSnvdMnG*hWmX_kQSCa|+N^XdJ)4)9y3U>8d#rzle7x2q^KrEns`EUx5 zw3PU4avb-V74u(*=p%eiYU#FnRyBd6;UdIB4yfQCGXH9n4ZUJ6{P}Abwq{ZPY7jB5 zMdz*I-9OLeF&sY}?{9Y^86zfK)#;)lSm-q_r*s&1?smb>lYt=%!DGh&%y{E*pJoEakj z@fWA}%H%+W1Ls!iG57J9x&9FI({poHG+R}esGHztCIMs;6ZMQ|S2h2l74Q@0n1bW9 zt(a#tpu1l$jUU2uNJZ^!Q}m|UapVtn8DoX~R|$Ucn&Mhx94jzUwlQ>nV>btaBK^hq zA>&tT#>&BE{#svbmzt`(ULIEfvPLHcNe|(RaU5UR>ggrJ;j$bM3OW4nV zfK^8&E{gny)i`0L%JHisWyQm@&7Z049W^rsM-K0q0)=j`u(=JaIqqolEemhk1&6$pP`n{o{J)C(MA3m*W?%1?Ui< z9yzCb!-VJ*uL|h9S<1fvThbc6GU05YJ95@Y_ZRGMi`{O+P-O}K0&ErJN^!$7?}c0q zsX(P>O-91i$cS0(iZc ze?goXgz3;A4R%^Dy!yjdb_;VNseT z9N=R821B;82C74}mHY77?QJkd;r;@Cas46W>cOASXeO9J7XZ!oW;~pX4$mppZ)g-= zJRU|-jfInus7p~7>H0kYvJ(8ldk*8*o56Qh97$vckqC=*-iG|wD@FXmeai|y?MG9C z4#@tfs8)kLtvM8^l<}_wA^#Odd_JDTm~p$8o3O${ob&v{TnHt6swEXaE0)oaYRp+* z79Y0#yDZLoHdEIh5~>==u&`ds#{pXv^i;BA5``Qu3pPKfKh*j)h9_f$TJ*{2J_DXr zZRZ$#k}~|#S#pQxXB_!KhjX=TY5n{c*B`=Pm57VZ1MPZn97;3~PZA1uUbT64jOz~{ zQLrsNsI=U!8c#PM|K&W=-;!!rhgEiXJRbkjvC~!>l)R>%Lp9#8vNZpNn2d@e`-Xca z+f*VGUJgX-8jj?O`7h>Q@S5E4o8Vs^1arYpF6UodMSw$-Y)(N3o^_6fP=LCt9KWC! zw)jYD%Q2YcUE&mN5v?i22Fvhkv9y?fZSqX_&xIyy`$SN`!T7bf*Lp?gzcd8kZ)xFD z+--FOrTC@oGp2VUlynj_=j1TX*^MCo71VD4ze2F{yNqY(&5_QpM87q72A48*$>?N( ze{uaG#}6Z%Rz8b#UNnsjc@`ERk~S82e~+VZ{tNmQqwG*D9%drO`7baRpMT;0LyVba zj}d(z`LFON2lU~H6en9t_?P~3Ffk6IHm$ljU)e!FF!gMd@vqKQocY%wdvp~D?*a#C z)t7jnP(P3QLyQofa2ofe=(HE&hiOzKu8)4`)?)nzub^H^H4$nz6RyFF+ZBl(xdguy z*jC;`yd?|RdQCmr+nQni<;Caq-~WP(4#qJd@Hab-K)=|MBqqZp`7i8V(J~(XOYsvm z#6(q!NgEMG+E$YP(){au^sd1RG^UWiS_jwYo&J?h^pA@9FRfpb(T9!G8I&Cof(ANU z;YLgHUz&e`#|?Hm05S*m@NJsFqHztd$Cvs3Uglr%J8?h20b1$NhXt9378*{WQ|8+)N zl5+g&n!qw?$0HYg`Th$Q7A_b4H8f7+mtTK4uf{+2l|udeY4s}wJO2(8Ey=ddj~^irgis7WVY!O;Z#>Rx z!S#n)Kq#8mU`}0zUmX(orGI^F_D)Qce^H5DieK<0{PC#pLGL1Z7ks&mB+JG5FGxWJ zxpLL>u_#<41iAeBdCq@v*gZd5D9b}U^c7ev4;a=mb z^v`$#3s92(dPF61{+Jh^M3EuidDW6D%YQ-k)~SN*BO^;=8^OO=_PVnCS64cbF*ttM zk&c{3z!o>&C>V$G{1?ZgG;9%yLj6Wb{UNXbK0n0KjamR;@tAPsLxTAi;x?D& zzj|N_JRn2b%LM;I@RQG6m+&wAS+v6ZYXM)BmFW;hHObGm?E96$80 z=I|Cp-+y@a^86R}bEGgoVTHe+qUWb9|Hb?(?xnpJr?=&Bp99~B7|!4zyyX4t<5#Y* z$Igf2yAuWdD#?Fg8y_j`XE#g(sG#8e+y@6(zo+o`Qwrz5a=3QBv*OtS)NhpBzX1zL zoqr4q>6;W~oUYflrI`Oh{LtfH1$#Jdz_Cxm9?k$+^gMg`1+T!@FCNC@h4C!%{6o%v zVa&N=pI(owacA=I%Znc(cIw;A?r=wJZ$Wm;)OOYX`za?A{+LtIuNfWSU$BRH!~ZVM ze?f{Po?N0c2SxJ*OhVE0d;I(7CGbLr>D#jbAd(aVQK#_Ok2c_!kBx^SjfVSs5Xvq9;CX{ry#ER7US{w<># zF=+pEmRqr+a5RFg+b<+*1UKZA?GPFwdrsg(Olr?VS zC*-dM<8(|S{G}^c~DrBlvjdqVo1-WiC{!qP=y0)RlYh2e5xzVWYAHtJJ;k%x` zvEt7D&!#@pQ1Oi4sK0yG?fvWUBv`K-DvdkUiPUusHP6htv;Rcu_t4qk#{b@MwOi}W z^9}kvx8t{h_dHX-M9oXBZm4%_J^jK^FW0ZsP>=I_mZ?JCd#EyWyIPmJqM_pMpiyyq ze^2Vu7?0oO23gS;`Ye5h>Rq7TXX#e5T|8NfC!Z>O*A3m2QVmX}6&lbDyvlhlkjj(*~Ist7HSYy0NRUGp?IdTZyJH&oP8kDOzhZs9#m6&nu!ao?x6*FVIK zBpOW>J%>NZ4OQqv*tLiv5iBekN;s6J1%)SmCu_|ewGHaV#@bL-Qo^8X09pLb8ARHk%dlH#a|L*K}8V@X8Gvg7Wy5i^ zD>U$AZk1c;WR~DE;MfCb52=Gb?`JoBOQH(zA$mVN1;W~)ppRw#WoVxIeW!N(vPSS- zzx6%9<-&Ja1vn0H*>7w>J*)q-$h@h)F0*E&_VR|`!*U}qU31-vBuv+(4e6T=H-KX_ zLLNXP?1P33K|A@!a%DknSbxeDso2M4oVk%|)#VNEJ$6Wn6zzi6RjJDw1N>qP;f70a zIRI$JWzQZ;^t&9FgD3nNk{BrxkPafbM=oo)x{LlUA5RC0cekTUN=9OU;^ZSJy0VQ*l)%5XUVXvSsl zJre6xz~##tg+ADavY&JO@U<>&P(dz(dVy{gkOoRU z|0sC!KW(3a3J1DeYy@MzOb36rE?>G=|NGv<)b0Is(0|Ww)4&40aJT<=x{Nm!^ot5A zJnuOu^+cIc2Yr5rE(d+yUkA1IEA)9kT`nl~|AQ`-7T)g`{}X|)qbbn zqRZZ=AL#P?nS%eZ`d(bz-_}Z!6XNZbr0$RZwmu&`|6m3_n1K&w;DZ_XU2L64{0J8icqN8m|QuvpQB+06O Z%S`zFE0UDq-{Xn`{Cl{VmhxZne*^UawK@O* diff --git a/fpga/hi_read_rx_xcorr.v b/fpga/hi_read_rx_xcorr.v index 433d6736..f637abf2 100644 --- a/fpga/hi_read_rx_xcorr.v +++ b/fpga/hi_read_rx_xcorr.v @@ -27,22 +27,12 @@ assign pwr_hi = ck_1356megb & (~snoop); assign pwr_oe1 = 1'b0; assign pwr_oe3 = 1'b0; assign pwr_oe4 = 1'b0; +// Unused. +assign pwr_lo = 1'b0; +assign pwr_oe2 = 1'b0; + +assign adc_clk = ck_1356megb; // sample frequency is 13,56 MHz -reg [2:0] fc_div; -always @(negedge ck_1356megb) - fc_div <= fc_div + 1; - -(* clock_signal = "yes" *) reg adc_clk; // sample frequency, always 16 * fc -always @(ck_1356megb, xcorr_is_848, xcorr_quarter_freq, fc_div) - if (xcorr_is_848 & ~xcorr_quarter_freq) // fc = 847.5 kHz, standard ISO14443B - adc_clk <= ck_1356megb; - else if (~xcorr_is_848 & ~xcorr_quarter_freq) // fc = 423.75 kHz - adc_clk <= fc_div[0]; - else if (xcorr_is_848 & xcorr_quarter_freq) // fc = 211.875 kHz - adc_clk <= fc_div[1]; - else // fc = 105.9375 kHz - adc_clk <= fc_div[2]; - // When we're a reader, we just need to do the BPSK demod; but when we're an // eavesdropper, we also need to pick out the commands sent by the reader, // using AM. Do this the same way that we do it for the simulated tag. @@ -69,15 +59,27 @@ begin end end -// Let us report a correlation every 4 subcarrier cycles, or 4*16=64 samples, -// so we need a 6-bit counter. + +// Let us report a correlation every 64 samples. I.e. +// one Q/I pair after 4 subcarrier cycles for the 848kHz subcarrier, +// one Q/I pair after 2 subcarrier cycles for the 424kHz subcarriers, +// one Q/I pair for each subcarrier cyle for the 212kHz subcarrier. +// We need a 6-bit counter for the timing. reg [5:0] corr_i_cnt; -// And a couple of registers in which to accumulate the correlations. -// We would add at most 32 times the difference between unmodulated and modulated signal. It should +always @(negedge adc_clk) +begin + corr_i_cnt <= corr_i_cnt + 1; +end + +// And a couple of registers in which to accumulate the correlations. From the 64 samples +// we would add at most 32 times the difference between unmodulated and modulated signal. It should // be safe to assume that a tag will not be able to modulate the carrier signal by more than 25%. // 32 * 255 * 0,25 = 2040, which can be held in 11 bits. Add 1 bit for sign. -reg signed [11:0] corr_i_accum; -reg signed [11:0] corr_q_accum; +// Temporary we might need more bits. For the 212kHz subcarrier we could possible add 32 times the +// maximum signal value before a first subtraction would occur. 32 * 255 = 8160 can be held in 13 bits. +// Add one bit for sign -> need 14 bit registers but final result will fit into 12 bits. +reg signed [13:0] corr_i_accum; +reg signed [13:0] corr_q_accum; // we will report maximum 8 significant bits reg signed [7:0] corr_i_out; reg signed [7:0] corr_q_out; @@ -86,12 +88,29 @@ reg ssp_clk; reg ssp_frame; -always @(negedge adc_clk) -begin - corr_i_cnt <= corr_i_cnt + 1; -end - +// The subcarrier reference signals +reg subcarrier_I; +reg subcarrier_Q; +always @(corr_i_cnt or xcorr_is_848 or xcorr_quarter_freq) +begin + if (xcorr_is_848 & ~xcorr_quarter_freq) // 848 kHz + begin + subcarrier_I = ~corr_i_cnt[3]; + subcarrier_Q = ~(corr_i_cnt[3] ^ corr_i_cnt[2]); + end + else if (xcorr_is_848 & xcorr_quarter_freq) // 212 kHz + begin + subcarrier_I = ~corr_i_cnt[5]; + subcarrier_Q = ~(corr_i_cnt[5] ^ corr_i_cnt[4]); + end + else + begin // 424 kHz + subcarrier_I = ~corr_i_cnt[4]; + subcarrier_Q = ~(corr_i_cnt[4] ^ corr_i_cnt[3]); + end +end + // ADC data appears on the rising edge, so sample it on the falling edge always @(negedge adc_clk) begin @@ -103,36 +122,60 @@ begin if(snoop) begin // Send 7 most significant bits of tag signal (signed), plus 1 bit reader signal - corr_i_out <= {corr_i_accum[11:5], after_hysteresis_prev_prev}; - corr_q_out <= {corr_q_accum[11:5], after_hysteresis_prev}; + if (corr_i_accum[13:11] == 3'b000 || corr_i_accum[13:11] == 3'b111) + corr_i_out <= {corr_i_accum[11:5], after_hysteresis_prev_prev}; + else // truncate to maximum value + if (corr_i_accum[13] == 1'b0) + corr_i_out <= {7'b0111111, after_hysteresis_prev_prev}; + else + corr_i_out <= {7'b1000000, after_hysteresis_prev_prev}; + if (corr_q_accum[13:11] == 3'b000 || corr_q_accum[13:11] == 3'b111) + corr_q_out <= {corr_q_accum[11:5], after_hysteresis_prev}; + else // truncate to maximum value + if (corr_q_accum[13] == 1'b0) + corr_q_out <= {7'b0111111, after_hysteresis_prev}; + else + corr_q_out <= {7'b1000000, after_hysteresis_prev}; after_hysteresis_prev_prev <= after_hysteresis; end else begin - // 8 bits of tag signal - corr_i_out <= corr_i_accum[11:4]; - corr_q_out <= corr_q_accum[11:4]; + // Send 8 bits of tag signal + if (corr_i_accum[13:11] == 3'b000 || corr_i_accum[13:11] == 3'b111) + corr_i_out <= corr_i_accum[11:4]; + else // truncate to maximum value + if (corr_i_accum[13] == 1'b0) + corr_i_out <= 8'b01111111; + else + corr_i_out <= 8'b10000000; + if (corr_q_accum[13:11] == 3'b000 || corr_q_accum[13:11] == 3'b111) + corr_q_out <= corr_q_accum[11:4]; + else // truncate to maximum value + if (corr_q_accum[13] == 1'b0) + corr_q_out <= 8'b01111111; + else + corr_q_out <= 8'b10000000; end - - corr_i_accum <= adc_d; - corr_q_accum <= adc_d; + // Initialize next correlation. + // Both I and Q reference signals are high when corr_i_nct == 0. Therefore need to accumulate. + corr_i_accum <= $signed({1'b0,adc_d}); + corr_q_accum <= $signed({1'b0,adc_d}); end else begin - if(corr_i_cnt[3]) - corr_i_accum <= corr_i_accum - adc_d; + if (subcarrier_I) + corr_i_accum <= corr_i_accum + $signed({1'b0,adc_d}); else - corr_i_accum <= corr_i_accum + adc_d; + corr_i_accum <= corr_i_accum - $signed({1'b0,adc_d}); - if(corr_i_cnt[3] == corr_i_cnt[2]) // phase shifted by pi/2 - corr_q_accum <= corr_q_accum + adc_d; + if (subcarrier_Q) + corr_q_accum <= corr_q_accum + $signed({1'b0,adc_d}); else - corr_q_accum <= corr_q_accum - adc_d; + corr_q_accum <= corr_q_accum - $signed({1'b0,adc_d}); end - // The logic in hi_simulate.v reports 4 samples per bit. We report two - // (I, Q) pairs per bit, so we should do 2 samples per pair. + // for each Q/I pair report two reader signal samples when sniffing if(corr_i_cnt == 6'd32) after_hysteresis_prev <= after_hysteresis; @@ -167,8 +210,4 @@ assign ssp_din = corr_i_out[7]; assign dbg = corr_i_cnt[3]; -// Unused. -assign pwr_lo = 1'b0; -assign pwr_oe2 = 1'b0; - endmodule -- 2.39.5