From 4646ec51031de63b65196cb7f967b3fb25c6628c Mon Sep 17 00:00:00 2001 From: Diane Trout Date: Thu, 8 Jul 2010 22:15:31 +0000 Subject: [PATCH] Update srf2named_fastq to try to detect if the srf file is CNF1 or CNF4 and figure out the correct option to pass to srf2fastq. --- scripts/srf2named_fastq.py | 36 +++++++++++++++--- test/test_srf2fastq.py | 21 ++++++++++ ...oldlab_070829_USI-EAS44_0017_FC11055_1.srf | Bin 0 -> 10240 bytes ...lab_090512_HWI-EAS229_0114_428NNAAXX_5.srf | Bin 0 -> 10240 bytes 4 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 test/woldlab_070829_USI-EAS44_0017_FC11055_1.srf create mode 100644 test/woldlab_090512_HWI-EAS229_0114_428NNAAXX_5.srf diff --git a/scripts/srf2named_fastq.py b/scripts/srf2named_fastq.py index ebf3323..7bfc4da 100755 --- a/scripts/srf2named_fastq.py +++ b/scripts/srf2named_fastq.py @@ -1,5 +1,6 @@ #!/usr/bin/env python import logging +import mmap from optparse import OptionParser import os from subprocess import Popen, PIPE @@ -19,7 +20,7 @@ def main(cmdline=None): opts, args = parser.parse_args(cmdline) if len(args) != 1: - parser.error("Requires one argument") + parser.error("Requires one argument, got: %s" % (str(args))) if opts.verbose: logging.basicConfig(level=logging.INFO) @@ -39,7 +40,7 @@ def main(cmdline=None): # open the srf, fastq, or compressed fastq if is_srf(args[0]): - source = srf_open(args[0], opts.cnf1) + source = srf_open(args[0]) else: source = autoopen(args[0]) @@ -55,9 +56,7 @@ def make_parser(): file can be either a fastq file or a srf file. You can also force the flowcell ID to be added to the header.""") - parser.add_option('-c','--cnf1',default=False, action="store_true", - help="pass -c to srf2fastq, needed for calibrated quality values" - ) + parser.add_option('--force', default=False, action="store_true", help="overwrite existing files.") parser.add_option('--flowcell', default=None, @@ -81,7 +80,7 @@ def srf_open(filename, cnf1=False): """ cmd = ['srf2fastq'] - if cnf1: + if is_cnf1(filename): cmd.append('-c') cmd.append(filename) @@ -185,6 +184,31 @@ def is_srf(filename): f.close() return header == "SSRF" +def is_cnf1(filename): + """ + Brute force detection if a SRF file is using CNF1/CNF4 records + """ + max_header = 1024 ** 2 + PROGRAM_ID = 'PROGRAM_ID\000' + + if not is_srf(filename): + raise ValueError("%s must be a srf file" % (filename,)) + + fd = os.open(filename, os.O_RDONLY) + f = mmap.mmap(fd, 0, access=mmap.ACCESS_READ) + # alas the max search length requires python 2.6+ + program_id_location = f.find(PROGRAM_ID, 0) #, max_header) + program_header_start = program_id_location+len(PROGRAM_ID) + next_null = f.find('\000', program_header_start) #, max_header) + program_id_header = f[program_header_start:next_null] + f.close() + os.close(fd) + + if program_id_header == "solexa2srf v1.4": + return False + else: + return True + def open_write(filename, force=False): """ Open a file, but throw an exception if it already exists diff --git a/test/test_srf2fastq.py b/test/test_srf2fastq.py index f29cc70..83412c4 100644 --- a/test/test_srf2fastq.py +++ b/test/test_srf2fastq.py @@ -111,6 +111,27 @@ IIIIB+++ self.failUnlessEqual(lines1[2].rstrip(), '+') self.failUnlessEqual(lines1[3].rstrip(), '@IIIB+++') + def test_is_srf(self): + cnf4_srf = 'woldlab_070829_USI-EAS44_0017_FC11055_1.srf' + cnf4_path = os.path.join(_module_path, cnf4_srf) + cnf1_srf = 'woldlab_090512_HWI-EAS229_0114_428NNAAXX_5.srf' + cnf1_path = os.path.join(_module_path, cnf1_srf) + + is_srf = srf2named_fastq.is_srf + self.failUnlessEqual(is_srf(__file__), False) + self.failUnlessEqual(is_srf(cnf4_path), True) + self.failUnlessEqual(is_srf(cnf1_path), True) + + def test_is_cnf1(self): + cnf4_srf = 'woldlab_070829_USI-EAS44_0017_FC11055_1.srf' + cnf4_path = os.path.join(_module_path, cnf4_srf) + cnf1_srf = 'woldlab_090512_HWI-EAS229_0114_428NNAAXX_5.srf' + cnf1_path = os.path.join(_module_path, cnf1_srf) + + is_cnf1 = srf2named_fastq.is_cnf1 + self.failUnlessRaises(ValueError, is_cnf1, __file__) + self.failUnlessEqual(is_cnf1(cnf4_path), False) + self.failUnlessEqual(is_cnf1(cnf1_path), True) def suite(): return unittest.makeSuite(testSrf2Fastq,'test') diff --git a/test/woldlab_070829_USI-EAS44_0017_FC11055_1.srf b/test/woldlab_070829_USI-EAS44_0017_FC11055_1.srf new file mode 100644 index 0000000000000000000000000000000000000000..dd802a7108b1791f5f0982086c5a22c4f7e3eb5f GIT binary patch literal 10240 zcmchcc|4T+`~TmTIH~AVlB6V3c4ipHR!OqUzE8>!gTYwKGNpadDMhlSL`Ywt9IOL?2q~(>41Hehs5|%~CD#tx zX0nNECl?Frar0x$%Q25_0KX1@coUG_9G>zA&&6u~XJJwsR=tk_7jCmm7k++IkG8&` zZGPK(hZ3%i+Cu3c)O1>$to8LXcj*hzIa+`PDXjRc&+r_8e;i!AkTs%o6K$<5eCJa= z)f5yQ@)Q6-=)qE%mw>Zuu2g&y8`vOH$sz>mmQbx5cD>kJQL@hxtXg&+oR4Pv+HVnM zZ@pbqqgL&L_30NO(hg519xv8!24C?UM>c?d%t%_AeObv=F{5Ov(^gz0XR7nuND1-H zeps8x#k;yD_4UJhz^>Lef3hFmIJhT$GA^uw14x!X5fT9NZI`4j=my+=84(aqEVdsS z_Ae=-b~jV0Q~^r=u;G9S`?gm{D<_Gh1?ClwZDyC-2#b0+H;(d#Xv@kBaeyVa0b5%P zrmbeO$*;tPLinaZA$Y{v7kD+HDT>jA=b0fqfZLt*j|)PVHw|`ZF7fUIN_fumieS2? zuii#Mff5XzuiCsiIKL+VFQ%TQGmm`qXQk~aX!vM z{&LcAU}~hVWpx;3d>DG)6sT$hdbkVuqkV9$SWh)!IcZs84RtQnBaS$H}B@365RhE*Ml|`VOTpc~p!pu)YpVtgJ?T*F? z9dq@;`9hZ#7SeZQK7lC;3mN)(oI?AoJeT?YaUD-DKVKnZN8dAQ!kThYFhvw}Ci=7w z8t41J783e#u7RVkk89x1e~le|pzrAm-IcnmwCwVsDzoavmG8Ieh5yH=OtH?s0ggWC zwYM%TWbqF(X21*oT-?mn9c_wsN1yhEkgnVt1VbMJBc$W$gbrk)`0+CIhN+{6mpfV= zrz-RP+47fL2FSdUiKC|rTFAf=It?N$EM(xy{F#2P23)%)bPcs+p_DKYnitC0}3l6)BQM2+6^bvdTz#Wg+9E zW>91;zqu0i(4~HyrfQBu`+Se<73Eq9VpSPP>L3-A`CSHqsev&Z024^~_W@2tB?TlL zrUcd-9Xn zrVf;Utha$JNtu}ERLI@8|4a(L+tKsr7RD17wZB%0a&9J+t=u^TVK2@G{2S=~Pbn@D z;Wuk$w-f8x_@GR6Hbua-^sD0JZD}4PS!^JckhtyOdz`*{jGjFQp7Y3IQ!VBVKnITj zzC1Usfuc*mT(Qcw2-Y+_2Ho}YA!cwv{jc?4$6-;h<7Oi`x!KyBRh&f#uY2F3y(J`g zv7+9HrIGKMBomP!;8d1{BM}JrT10t25veZ43HB`sBqxfIVeQ<~x^>VV4)f!C9SDDD`dnx7&eD(1^s zOfehYJ3-apPZ+s5`E0hHxnh0O%Umf_#<@dT+m~)8*@`M}1X5ql$k?!h-0G<`MG|J$ z8T~I~hYVaDpaedil@$9`#N;V0x+=eD-R_pA-1n9bsyfCExU^Wy*;e7%%)}EKT zVBSczN~*8oBl45a*NSyO_}TndyzWUou4iVhz4f+oV{PCNxWA4GM;P)S2o6jhwiZsw z4>;scu?+qUY^UWZmA7Hk5|PT$+!2J<^@ov*CGYBJE^kB8jA;v%L6fNz=Z2xK?54Sz zW2O;VnYTXS{FN!b!y1)SsT6Ue4Cg&sOCCYzcFg&I4m*5zfemQWZKXck@)-Bw-j!0D zCYjJ;*NNXdOy;>_OVxNnf1=vy2&~4i2AVhyJY#BsOk(v9MJazC*nP&q(;uRAvpf3H zlA05xU#0GVmeVnV?(CsaIvP(hZobUAnpAL35Zx3 zr?Z>Q@`96(OrGBN=EzQlim4SsLVyz?k3hne<<}A_`$34du#nM`7Up(6Ib&KS-6pbx zmPY+*HD+Yts9BhZC5arL3g{teKQ3ArXY_n-eK|ACw|7#YvC%ng_U_qibPoA?Nl$ts znRe_dY{1VjSmi zF+(L3DTA-z7e={pU-}d~ny-qs?%K18k>Ed!*u9*AtnvD1?06tN*TYS; zT4(Xx8JSlP-YX`YR%^BQEa{dvYEkL-(ui)~MTc+ibilMI!W}hx z?qxN$nA7EiC%P5h?msyKqg*1TKb4~;y@0p+foX>IsmV)h<@}4Hn1d&;Po*}__YE8~ z{0LRH_=)%cl^5a65|`P^LZ@X9#On!DG3c$ z+z3)>C4@W@2Kz;bNI%1DHsx&fEVff5V#5+|p7jmUC60S379UU-rKNT*7Cr3O?hTuA zpUC-~`;4-Mv1I>N^6eAaxRh^894)7_KDDP&29=;iGe_8r?hiHPYOt#w%4o1afi68S zsrZMJZsZ&8-5;$y%aBCsq|=xnGQ6F#S&nuyIQH}=5bTDVB6tC-Ct5+%6Uc#wjJ(;{ zkCRc+P_QWjSd+obNF4XO+8Y-P?ehGq(4QCviy6D zDLrF1E5$LjM?BMg2aC{!cdX19+YP~yQ$i{z{t{!wKj4fo5N)*f#Uj!(>IhcPawvNG z>Gbv7bzvubYv)JA9OG>~%V6ArjBo7*X_YUq?}l+F=&8dgH?SYlry{vGloECAPs~-0 z-;H+D&=WAJ9}PH47`z4WWjcmU>0_Ut)##J4(E_(7dSdlrUtWFk*+c<5aCzbsTjD_w z%p2SGFklA=bs?qZRfXhcdS2Ur855;^#6kh4HoUMqSmJhSz@};J*3@UhR9yH818qaI zzEKo>O@J+Xke!Me0vJYtssyLD|?^U&w9mwGhPApC`>`J^``FfBzR?6`4SiH40qkX97vy+=@ znb%y9glekb@uWUi%2kC<#&lMzLp0Hg?rt7hHIv#Kx+@Lq4>M;gU!+>ln|7;EW+NxB zr_di94$F#k2^UMA({menh7E}Mt;19HOj=9-%tYM^HOQG7Es zmzY9}cMA=7#)nhS_4K{Cb*U*}_H+CUg$MBb5?l@Mn$xxhB;2HS`3tNTXVF#S(^|p; zHkz@DpXaiS;)J}XxM02HLEvIxy<8@4g=!#}JQ6Pd3!?lVh&0k#`tX8fL8aLzIuOM& zO12^0XK!CA!alBf&dVhe4|MSCwvd}(r(bFv<4i7X8BMSN&JA8-w zrdikEVWxLjiG+&*0Up5>cWbrd?EHt0(zA}QrYnB41#DR<3*{lNfx(ap@c$(^^l`lt z-LJk)^H*TzEvG{JTo+>IU`l(3DGXjtvxSA~syA(AO}@hqiMUc3!IwVqp*D+Ap0PcG zxbc4Fj`b&0OwQ@ZoF3JP!}X^gef;DtrfUM`DF^2D1>NsO;a=V0Mix@y3;&#iqkN1K zn=2S>v0oChP|y47S-K7^`agBi07JEx%dYhiNCJzsCv_O*^SySRzsCRy$t;my< zwp=MgF_mz8l18yZu5@$iA7J+?zgez(?0WtE^Q5lX|K+(_lVK}ho}KJ?tO3o9RH>{? z&pCvTnd1m7Jg<95G6I-ePPg*P|{s0Lpv5SRHo8-Vx;?>XM9EuqNb)azw1iymHT^iXvmf-qn1O4vvGx` zn9>;9p%{bQMfat3$lfvJtVE@M!utjV`SoDBIrJL$^z?=a-K} z76v9%2=%g&E$L>pkB_w_qK%7D^s$C%#u> zWxv(=;&^mmN2iocx|rwQ=j`#9c28)K=Je0lR*Z-9@;_)Qe%jvks&)vYE*+P#J2HzK z6d4?)WDF}^-mde7Ut!^tHHvFeYz*1j;nb_pW#Ll?jKiX8?rsF;#4l0S>N+Xn*i$W| z9t+G-Ujr;xi0DIers-YX=4YdP%vAqxiMUD|r&R9WVk|d3J+qtOag#Ui`sx^yU8Zuf zl?~Xm21Bz&GiiM;aMi0?6YX;tJ;-hhOEYs7?b2`=t3=C@HW{yo(N1PVCJQn8rDrAl zU{n?)ljC?nLn>oJcj?Hs1A2X_QWi0_*lNSCCSluh#6JYyE1@TcS+vs>ZI&j^5OU*g zYZ|q8_e~ukcx4f29pi1q6Iu$#y9!z#I%l548l98a0o3=vz02nE>RLi9d0!a zUN)V|g9_v!E50yaZFw-1SJFKg$%ap(-B52<%J7-Ulz2iJ)kVnza40cFzf?~RjhTDb zKEu!7rpQoC9hvX!e;GBtd*KSKZb4>QTLlH(Bbkn@a&FVv>9 zLKXFLCKHA7yey$s@3%BbwC z%WBB5yqoJ3;5(8T{EBX<9US1on^B-l9eFeDOv`<9#xB6woble@OrUy17Of1J(y5<=b{Jon1rsbswmf_i| zVaP|3!HtxwFhwb=?6pMz7ESL8v8;O+RW!??@4M%$^>|xJ!DrN#-x7W9eU%c-OAOx= z?!0ccRf+zeQrfGF4RDO?S_!h_M%$ruW*5u`wXw_nybRz}KtM92^o#raEQV5xWo*kA z$w^R6Iu%`&^{FpG6m?A)Q|5O!eQUN``op2REe9?;V?s5KUiYT= zo7fjEs$9sVXStb*cGw+Gtvc_n9Cl+<2VW03nIGoE_IBvi$kp9EDIcN-O7daow**Ch z;o#0HF-Td=7wj~^gNTtk(v2IzT+nRj4Snzzn`zG%*LgFEbscN zV-m}Gf8tX8g<0iO*g0dK-x+ndG6;?>my}*nDmhm%lKw8;kCfSfxN%#;*I7=Y>Uo*P z^heCPh{N1Ct^VB%2TuoH`93OXMYRt?xR`lPQFg6Q{TzRYEol`9&yqi?_0_aJN+i;6 zF8UW;N$v2MD?N3Zj-4XyJx1L*zw4q-tY7-|kyE);_oT;i(f1uMLtdsvmy_Rc=X2Gy zhU7L{wZTiq<6{kP3<%pmRk^X$3^ArOP0^sD{b5x=qwDn))e?quc5g`LzFSj#AZ|0; zTR0ma#3l&uiicY-oEo&2E;O-(IN4+iI#@RKNhO=q^!H;4YOzlD%TGB+mc$!wSrco_ znh}=knX2G99MQ^Q(X-l>R%8jt7KE@v(wxZxSfWq>Di>LVUQ1NJN&itp${PgEbAms< zr#GmPm50HR3QE5OU*->48kkQB5($)qyMA)Ou@EK-%lH*s9QMr|NuH8~Pvp?7Cn6m* zc)S!tJiR?GT@*J~X}-Z7Nj*b8=6V~G=Px*J(`&NiXBn{+zso0zz*4@O!ABGw*&NFs zt8)348$ogXP31O<&btgBXJ?}I^(-L!a1fhT39`BTDW_XHqIw_JHd!PqL9;RZ;?TW? zx{-}M>+@A?3}d7GpjU$FVMy61qeIFhWDF)RMC zHVc_+9g10KZvh*f$%D)iVY%Jkv*J!L0|gJy47_l;em`{@6NZDRZS`8`5HrVH`$v z((-fTIs|bVjDQ*kbphv>HLt@?cluF$L3t^~Z_Z%PdhWE+&`sML+vy#gPY)`(Jl>|L zMfI*vrfPu)*JX@ivcQMJ(BU&4)T9I9jVdlTg54wmC_nC&RY$Pb|Mc=Mf9hjzLqZUa zYJodj(0kbrt(f^wUJ98&q$KL7a-JhX|e;{C%iStkfSS8TBt8X4J|>3uUU#Iwk;x`X%4`cgS4 zGx{;=7CQsdz0bd%e0(OhQ2#FvsJ7*bN#LTr_iR1vCY))Bed~~KQG66Ukc=(n&CT_2 z@pRPK;Ul+H`f=dmn>25OjIS@pGLQl+CvU=#L#m*b(}jvXo*B;@sU3T^)*}rHGr!T0 z;XXg(zisTY>>@6N^uv0&XRy+jb_#&s$7j|JiZ_BxFC((=tJ#l=;qV#p(vZ`| zF=ss>D(8$C6XV`yd5AFq7!ggM*QK;=m8T8wZ}FRzVO=9Xe=_BVO!XiJIVo3pWRu>m zmg!ZKMJAfi9Jb|-p4}~F9r~2((7BbFkOJU@BjC{F?3a4_XWyzEma5R2O)ae_Hzz86QeM3j%p^QsQ%Pm|J!|{OQhuD-8Ttu{Au#Mrd7R>#Vr|) zTjC>zH?4agXkCpU%{T3murEEpfw!-Cc?tE(S^3Umy2}mxshZZ0x(j~?o($O2nY@1U z*BQimU_nBee>)r>fNIl7>AnbGVGfjhlj?E#{Ma5Bfu>4K_YcE|Rm)l2AT-^5vug zTD0CGOr+LiF(#h|OaRfIQed+WnMAEx%|~%reY*bE079?hzPuFxC$qB+A@zB;P86`8 ziGOyBSs^vu=F8Z&JZa=0$CZNb#Upc0BnSK5r+z{se@t%vYnD39$sf1qf8bBaUSccD zdwZoA^Y&*GB=-voFcpr%c9F>;&sd|;vTuHaTfT+(8bnVwG`m(Sgy{6OxzrSv(y3!J zq;7O!X~^FJsY%Ujh4?IOK5Gu4~`p!msjAxhO{QDWhxTTH@d~9nf zZKh&3#HZHj6N0pdE*7e+o;2=RTR?_pbFTOl9@KR_h*+r~p*fMVoGcOn?E$UX4f zmPwDOpy2bi9X&QPi;U-o!y{1HY&!=+qiyy-bEvM_lo>Ibfx7C=6SHJ8c@Lu` zJ>m4J1B*3MvZy=Zngnp(osA?DRcf2YVL3Z9V zRfW(=uvBp)r&x^zq)9zZ<^pAVYdj1LfmiaZuBj1`T4~$+?G6=todQi7Wm}p9pA;dk zpDS*+(=WCQT%QF+p}{KZ`kAGUXwW-I@EI%pJCm z<>c_rktrER;h`6AI-BRx4`Rm>QZlPD1{hYA2C)jXo(0=5a7KT|^20h{wb1G5eGq4F zbX%cTNTWl;oVg#=sOl4F(vxuIw$9YY3k#I#<~gY=d2~#wdD)IN-7B->AM<6rD?Zb5 zDzCg~^-jl1+1N$h2R>-NPnOSSxwbBlZj5>)U-(&^rCg}!F|_I79}eR>}9(`H&+!;BeI3l*xW;$Zuw%5Qj!7Q=ejR!nGOx%@&)f9|6 z^YQqM{f&r$?xPz1d3OYPYeKc@5>5L-tA-jym&kH5K7Jx|??P-z>vfkH%b^faNVg@5 zP?;LJt>&ab3c+q?+oOl~gka(O2`2Om&fm8bi{9(OYH>G%vd335TBX_Sf!(Ytll+si zCK*r%3yvO_T)`sAEE45@@6d?fu>>nGy)J1ANeNDDlN?EY=ykbGy^;F0^Jb;xi#pU; ze@1T#nos4!z4jE^)L)$QbV`d@OOg4a@U7d7-b|#=VB%Z!!_vC+!IxM`bG``u7_v*X-S0Xs_`b5l+tRckLU~N;bMr+Rt(!%5;G{;#3#1;vhjw(P_`R6( zrQOcIJ-nNn6H=d@U*pvDj}bxO{kV?w zFMU3^`qzHRQkIMBNCAcIKy0zkRj)PWN?iFeGjv5d%&ac_nZ-AW=n>V zhZ+p1;V@J3OoF0p&ho+3^!zmi{_jl()SbbV;82+TvN`>;oHi*VMI4+P>{efYGaAdL XkvUlV^p12qf4jMNqN@B9t>FIv3x^a_ literal 0 HcmV?d00001 diff --git a/test/woldlab_090512_HWI-EAS229_0114_428NNAAXX_5.srf b/test/woldlab_090512_HWI-EAS229_0114_428NNAAXX_5.srf new file mode 100644 index 0000000000000000000000000000000000000000..b5d145f2ced081f65209aea4408e8b4178d928dc GIT binary patch literal 10240 zcmdT~&2JmW6=z%kzyIT3zyA8kz0y442bM6tH-4p}nGnQeu%_E+mwgtsWbW_WK7%z2n1G>)n~AU(TjdKc57V z)ECR>ViumuMXbYGvHltcVen#PxQ3u?REv)R0UDXiIX>=modfSKFcBOA!66VF0>L2=90I{15F7%*ArKq_!66VF z7J|b@I0z00!Qmh{90Z4p;BXNfE`q~FaJUE#7s25oI9vpWi{NlK zaZsbe(dr!?J?r<5JEO;)QNMFK=sh2GKRsyuT%jLud_RWo(;b7jRD+g_Y6ju9`eAAyv;=7w!`|`Z6rgt! zEP|;_pbzqQG|fA?4B|;N_6-8P4SaceAxX6hIo~v>gganfTd!%?%HMqP%wamp7bn-# zaAoeJXT#IZ;9#`>^tju5-10sK1ML;~Jefrg+msmXkNq9*Q4q`hAPhTE5Z=U-4A5X1 zX~%%@VLNYw(=K>K#jYWCnPIr2v>$9M^nkxuqYgtUK(!CZlSy)OdI?8=3GE^T$;yMY zsY{oGS=otbCqZ)gu$}LcE~gT!8wO*adV2Xep9S-j=m;8B-^|qM=SsbQa~4XUd3yF? zP4V>NB9@6-IaTMw%BVY8#7Q<2Y-*#*OU_-Fs;O? zt*19tbe0gYmL55fNLy1${;2?{jI#WNtT!fIt)7;Q$oEU>bB)9?ZMr%`xQSZ&A(6@v) zWzeYa!*TlvA9;7)W?<<)xVG$EdTukW) zFJ*D|<>yLyoo0-$c(@yGxZ?`R+AZSCXf zNKrv~Osl*fsgjar@NQ{wccn{sDu%8j~kl0ghJz`o>B_i z;q^R#&gfi`Y;8VhRxVm~4qiJ271`AN^xmp@QyD})cT9NC08vawb#Oi!IlTXCbciyd2(U3s!ZjU2vD~eETG?2=tXXT%su-=XHOdpF%}rv#sWcs{O$8Bf z>`fjs!MWksHUPUlPrw1*mM3h&AzBUIL#=EHwgvYL!m#uIUho8?#0Dm4fNl|Y3v|Jl z!HJ>xf1TJ@s&SQ}z70z-MpQr0;1!(KmRu6HWk9wyXMfI{tNc_e$!3mFoY~+L{29R( zpP;`<>!Tq~x8w-;^*}R~LXfsFyfB$b9K~w*$tjQCBtr-T7=mwO2-78EORh1SamV4* zGQR^uzytBN3;`Su(P}t+Bdwt(a^uwHgu>n%vHpHA1aR_}gENS;r2!PSYgvS=XF8r} zXcP0TC<0z*G@l6kjgRX5nvTRWO>U3!R4F?EK90q z_Bu1wNNc57(+)b#RZ3WMUjv+_I;jSKxX2%T_`$tthA^SFw^XXe4Dny@g=kU$ literal 0 HcmV?d00001 -- 2.30.2