[Python-checkins] distutils2: merged upstream

tarek.ziade python-checkins at python.org
Sun Sep 19 10:20:21 CEST 2010


tarek.ziade pushed 4e01a65bfa99 to distutils2:

http://hg.python.org/distutils2/rev/4e01a65bfa99
changeset:   606:4e01a65bfa99
parent:      605:cbcd1ec808dc
parent:      386:04327e2e55e5
user:        Konrad Delong <konryd at gmail.com>
date:        Tue Jul 27 15:07:46 2010 +0200
summary:     merged upstream
files:       src/distutils2/command/upload_docs.py, src/distutils2/dist.py, src/distutils2/pypi/pkg_resources.py, src/distutils2/spawn.py, src/distutils2/tests/pypiserver/test_link_priority/external/external.html, src/distutils2/tests/pypiserver/test_link_priority/simple/foobar/index.html, src/distutils2/tests/pypiserver/test_link_priority/simple/index.html, src/distutils2/tests/test_spawn.py

diff --git a/.hgignore b/.hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -1,5 +1,9 @@
-.*\.pyc$
-.*\.pyo$
-^src/build
-^docs/build
-.*\.swp$
+syntax: glob
+*.py[co]
+__pycache__/
+configure.cache
+build/
+MANIFEST
+dist/
+*.swp
+.coverage
\ No newline at end of file
diff --git a/docs/design/pep-0376.txt b/docs/design/pep-0376.txt
--- a/docs/design/pep-0376.txt
+++ b/docs/design/pep-0376.txt
@@ -402,7 +402,7 @@
   ``.egg-info`` directory that contains a PKG-INFO that matches `name` 
   for the `name` metadata.
 
-  Notice that there should be at most one result. The first result founded
+  Notice that there should be at most one result. The first result found
   is returned. If the directory is not found, returns None.
 
 - ``get_file_users(path)`` -> iterator of ``Distribution`` instances.
diff --git a/docs/design/wiki.rst b/docs/design/wiki.rst
--- a/docs/design/wiki.rst
+++ b/docs/design/wiki.rst
@@ -399,7 +399,7 @@
   the folder hierarchy from the module until we find a setup.cfg? A setup.cfg is
   necessary if you use distutils2, is it not?
 
-  -> information founded in setup.cfg will be put in the *FILES* file upon
+  -> information found in setup.cfg will be put in the *FILES* file upon
   installation in the egg-info directory. 
   IOW in the unbuit-egg case, we would need to create that dir, then use 
   pkgutil APIs.
diff --git a/docs/source/_static/depgraph_big.png b/docs/source/_static/depgraph_big.png
new file mode 100644
index 0000000000000000000000000000000000000000..8b8a27ccc4f695a149fc67f721f40e2798f8a8eb
GIT binary patch
literal 181858
zc$|EEcRbep_x~lLvPX7=hE+yF*(#M}W$#f|X7)~2l9fb}Eqi5V&y=#0dD*fuBQk&I
z<?g;e- at ks>t?oy4d0+4M>vhg^oO51+6y<LcpP)a1!C;7Q- at 2}h!H}9`FgRSt at ZoRd
z-)i!}|L~3O+`NuCK>sCHr$=Hir!lv$OR2cT{lvO at +<P!0zR!h?6|}18?pAhUPdi4G
z&8nzE8?Gv&C`cpumZu!EeeA at n=sf#HBKD<cT(O~SpGc0pQcb*3`pVOc5SysoEubA)
zTr07Kow;(mr<--reB7;8ZzgJLYAQ~|Ja%fhI)5i6D<>xhkNp4p>U|d2O?;a5|M#+x
zrR_fV|9wXmZa{I+)BoRVzV6N({_u&`*4D(ng`~a(L%~=jEv*xQjX0Qsp+#O=qVCd~
zni|FI-tNW3#M4__Tc?GEX|-~x;fpgWt3Cnu at Me{+6>4zv#<q1XY_Rk2M7O_QcN62}
z<cur5OiKipkzc5)s!HrKi~H{C;({)Kfj^8bECi!V?(8&nbmTK75dZJlvDQDGL>#W3
zl9iQJgqum}db+mu2=lnt!vFLA7o|~QM^79(_PDlILRwlnvDYHa%j&-ssmf8~Lf at sV
zd?fNS-dp*=)>c_eAO8cvSYpiB;$lc$oh0T!=$%ro8rzvOcq}X|D;pcCKd+FHEA1KH
zs-$<SJf*3rS?;pR`pDisA~Mo<ch}>-sp({E6j%A9=@W2eSfw<{-)$6BRDNS)27dnj
zEa%SQD=8_-N9SslNLM;7#x;ywKYS5?HYz+VBt-M!!v}V5?(4U2AAc4c{Pxw07Zg+{
zPfDft)SC%3Sd>Vo_i%LU<*E_t>+9p;;YnuqvT}11B_$;tJAPcs%q*jaf{7_~cGgV5
zW+V>B=D&?i9+Fd1B5!{y68QW%5!@l$`SS$P7j>nrtuL<q`lY0-JfTuB-m$W>B6;i9
zv4)0*&K%84#g+pUe0+RQgMy at VbQlMV&3#EuUA4vV{9SY<F%C}7hPj^fh}W-!e*UyX
z&)uDNr`+QY|A`YPQVo8$ojx)Y5fSk;B;*Eszmc&qIRiuR_grl at ad9R?LqnlyjqBIH
z*{Vbt&4&)X{CvORbL4rupZ$DsSM2d;Iucs^32DM3B3RmNv(>V at s$A{O!@V3F1X$VF
z$m!{y!CIy_kB&0?`1q{+{*6DT(?*Q>YI2F2I at qByH5JdPQa7vV!jn<AYNi`EZhUHM
zyP>BSPesAQ$@zkbfs)eq_u|l<FQ=S`ee!fGSvfhgeDuT)m#N~UCO7r{`}a??vEkD2
z=v|kW4|T9+eKK(N>{<D+<3vO`g6oVF6h}(5h++B71#gvTh5U_jcJ^C?fx^)gO_Mr9
zrh>6=zehz!H<}3?!N}gZV_2eP$V+R$|2kEZ`^E+Z`DtewP982U`#~GDVIK_S<cQt~
z*@OyO4JJA+d^f{ZT=pHl{@?3vIIK-q%1yhePvoiT>661B>~AS3+?0~S!PK?15Oj5Q
znRO)})hae)IjPZUQKG!TrbIk6I;ua<^-5h1fWzTUmCCw)Fy}BlMCY6XJ13{Ko*vUh
z{hH^nX&huEnE3ek?$cQLw^x(yL|>fk&8E5PFpD824a4{9{Wt61oC|J#V%!qSa`x=e
zxw$!E_uo19^uzP>^QmI2;lt71($R5j;0B#NeHtSrCAG4?jyERyQR%r7UfxMkNNQgC
z5M_yQjXb^TXDC!2JYa!H9~c-Y(8|R;e){y7!_qL_>gwt(Il1d$Ok$nY?#}WPH?Mzt
zidAE=%I<yE7)Ux<=YzX7875&n(R4gc!kdXC5Er6;wlfJAZuc>4Xo_y7li6sE*kGv@
zVXj)XL(f_2;G-xzZO4y5;Gg$z?MRRa`}$Q5BdMZ7RpGc0V2!OvvKg(J?aOCwXl#tK
z5<ZOU%bX at fbf)cb5%}?+uMN1mx^_E36f5_JW at NCZ-yzo{AtenA3k!SF@#6;v56?|Q
z!z2NvSG&r7-|QbfvfY?ho#ShZ6}q|bJvVL4DUjq;czF2Ztt}UTEv_xg|KaE at 1R73@
zgZ{n*q^z*2>GE%%V6AV$on3WadLDX8B-wtZ9X;K|<Rm#IC2o{#nudF>8rMa=s;8Gs
z+sD4Xz9j58Pt_hT7JM&V9<YIxo&A`Y+qz^&yjWMQw>J@^u;Lr4sK1{!>l7Uw9e7H_
zX$)3T*kg-VK3?>AY+JymPq)sTJI5+6u6N>EOG`_N>#rZrN`-`k)aMCg6%<-tUos7U
z^(r7+J-=aSh{1W-j=*hmDWr0#f^ZY-%uMJ(L4KLF%;DD1eSSmu*#1gqp5IH at VA!li
zi;`prW#y-u+;(<$vO=2*J#!AiUc18bal$?jN8|m4#=@>^X8^Y0`Oy8YFOM?&;uA~D
z$vy9GJ9`LO_y%P|5%KY%k>?dy#l%j*3N>WjOSfH}xS^t=QU}RzHB{<&g09TG2OnW6
zP%s%8S>pOVvzO0h*kGMzsjO+DX)fvj4 at 70fq6j;F{P?X~x9U*h`}yIyxVm!eQT=aA
ztj+!X{kJyy^b750<bMv95Yo}nAyi!+sqV^F=UM+bK#3OE(lV#}ZZ2hDqYo at R%jL at _
zIXF0O-M!lkDT;#;bN}5`y*U!>zFbY?JnAV_Rb743`oh16*$8Q{wOBS<VBA9Tv%e4*
zQt22G(e- at 2>eT(jk!tsp8^P4FLM8GSbu0W{zNBIl_n<`=zkORuQ<IK at lG1L}b8~#e
zeYsg|a~QWoE7##(&{J#eGM!sH6V1W>Rj!u+Lj0zt?!SzVPJvve<u$-8)$CZKwFQ`v
z6)LKy4Uu8I>Tv5s7PF`e*}2H$*b2wS0>egLqo!l)OIQYg0$HJdf at c2=JRyp-PZ4Zz
z2lWwbH*Eo(W7<q0lo5o(Vk(=JLzE)G>L9S at hxY@yq^6~fug~>#PXjX;7HT8{c$Yj>
zU!U*2ll6##nVA$`X|eMEq1(6A2d1XRi-RQ^W!9&hmPZh<t}pZ{&=(9=yKid0=PAv}
zA*ys<{s<|I_9af#l>)Q`o6Pe%h at sWK+WnLJ`}?v&k^e^eJI)Iinm>jxtp4hdF#huD
z0_cmI3F4l%Xz6NpmadJ~c-nk;!U+UVK+5#8VK$JIIUp>IG=P{PpmuN3hL+C=Pk=t8
zyX_2XMeZ;K%i^z at 4&&+8sIAo&X1ncmbGT&#NK0CA4`F;#rstiVifOU^X8ns!KSyhs
zAp{#<UC>53Q&7PBoJov6^4wi%W##S*e>&I?V_G|+*P?m6F)$)7?m27}+TjNe9-ts7
zv&P0r*TF&<(`p+6xIy4bT3BQiGm5!U$)u12(ajE(@!*p%K21$MgZ4^XTwGS@^?%Wu
z(Z$8Z)i~dqz1k);I<`6Lebda0T_%LypU?QC!KZMRm0!Q^RRj at V1l$E|Y8)(i2=_n)
ztNonr5<Wz7bE1 at QqH^X-Yu3Nf+C`k1kx^G_HMG)9qi^u*N1I}%3N8QrV;@6L1w*V~
z%kJgGHsPx(D?iy-=$o8L at cszzg)8uSY}tygHc`@wy6|2y0$r2%IiQ%3kg(8g!y-rL
z0|j8BbTIV=Uc&~w=ch!dtKBvx0&iV-U}9p_7(lF1?RMGgV9&X!scCFHkolu=m{>ql
z)Ah92n}^ZD4+}#;!dPchyBC11be at FA3>>hO@$eAEC!&1}z;q|8^{b>)&zP;SEo at fm
zyLS at +c>UGxLJ&R#-rnBBt}}0M$;vjs!`jZgbzU=-{Vx*FM80_Oq(kC>@!|IzpT_aJ
za=S_12dz|R&Ys0SI{9M%d2%xA?%Efgt at -Ty0`CK_VyhwQo^*wlZ<+Ui`!XGV{ksts
zXC)+<^}YU_t=V0>qf_B<d~?{I^xfUqkF8z&X=!O1wO(R1``Zt9e-GK14ZJ`5P9Y%_
zQYA&R$V65s?CI>Q-YMz9me5m;fy`b{qqx)?`ub=lKZZ0;eU3ypiC=V(da%D)t5ITc
zQY}v>sK~U#2teKC$7 at qwldrS^IEVK at mE1E|U^OIY*<XO8U1}+1XU7LP8}ag`pARkp
zdjHeVP_>HJ4wTc=(=1o6(4hod!dBY-_)6y&5HPX7v)uBOoQ>CERuPYYfD0 at 4KZkI`
za<t}@P4x!#i^#~;=-S<*J at 4)|rrwV1x6vX5(yXV^_wZS+Jz#>Uj=Ac{spm3&wAi9o
zcJAH3TKz*5$i>YOchWD>JU0~;$(X!%?c@@~PeJ70t1u54{PSyObvjyqBG#tHr{16r
zmUnd`n1}V^MR{}$?4vhwx7;BZU3M1B3Qaqh9Ok- at c8LBWI(PRC9zw((>)Rlz8{R+o
z0<eaU!f3!Wp&4R)Y<W4~q30i<cU8~VYbKT0eO&Ue&j{dsWo->b1n;A%ThD3vj$F_x
zR;&p6_87r%2!jxc%UkmD0a)vn$2hpWPCqrCGfObRqsR(H{4f1Rtgsa)0W4aWy?1?B
zt_6&xMe9N8zvynWc)?9gShGFPRRxcY<ncMVn8!jRF224FLI39Nr_Z13pByDDcr-1G
z?%8{P#+%o1US;dg9}aBsxn`q6gFkE2F2K4Gz~dbE-i-lGncAIZO^(gz3M}3P){WNp
zqTBhs*a&dV=l<K0h+g2<oC#KtpPzpP5^18SL;RYpEid&&ou}qK at 9Kw!wEu1iRvGRN
z0_E{?%?`;>sg<u*?#I#n-$KvnFP{Ky@&iv54+FH-U*jnvXg&P&V0T7>_qAO4!Tt`P
zdG{IgF7SK*cO_=uT?Jth at 4fG82`gg!@p(O0e$^9*E-q}2HfWp{wD8`$lg#LuLG1I|
zjELgl<8xvK{)Sk^UCmrIv{o9$X5`7q$pr at WN95iJ9M{v+lNHLnT{K{+QD}5j&wcSS
z6*aZd{_dZcOQs|q9v&~P>B*HM`^>Hl0&%-+&!rc-{xTT`Cf=HnI0zE=+-Aq-45 at i}
ziH~^vy6f?K!1QTwa3eStJ^-w=*c*pzH1$@*nFQij9CGV;<Y&L6Yk@|sFFA;YyB%M*
znQ-*idhR%Z?J}5X3W^bP7X%bd|6k?<X;Oh$RoC}C8BD_?1O93y-+M1Os%YW1tLqgY
zh5*=gT%fD1-CrGiK)HWE6<%epJ$HVO1IOHi`}@%r8+gV32S^-!*g|FSHPk$M&xJ-k
zD8Y_x0dCm!={j>@xmlCXwOyOTFMlM=sPX>(@d}5zCpkIXNLYwC{WOr&HUyu!HReld
zyEJ?SsTV|=kgAG`iiv+S;Y}(C7K8QKuGw#yl%+PKp?!Mp#30KNNCAhoJQmcA2H*TK
zj5!PvfmK9=A-`(v1bQPV)CnH~raDk9QU6f}B8 at As`P;u2gTo~DX_-CO$(8TjqwCDO
z32f!E|7XT-f7g|WNt8B4CZqw95<C&wwZASNALO8(>-0&0TyWYrNbv3c`32ui0BfdE
zV1Uyecf}X9G-zA0{OaHIj3j~GciC7i-qWj|T?AY4oI!}hdv}%KM at K>!DB1d!mYZ`X
z5e$fx(Ps(JukLM*8uu3%$_gDOUdC-m>yN=aHBUioA(3SN<7<6}Qp(tP*X)nZ<Z~Ix
z$wv at ZzBqfQz-Ck&R1E<T*TmS3|5a4@)$;X1Afb}9%dEEmHoHo#w9{f8hW>2U9)#2$
z>@7ci_N)O`va7=JVOs3JR8y8QF=2#w_hu+#JxI4zc(?A{IjkTj7uPBT!K6Qpo+OBt
z0;feSKR-YA!>vy5O2v>c3O|k)b8ooMKR(-);xZkjPTOhS6hO)xT3jrE^ak6_(54}q
z+7OwSo97R2(BYy^`Qt+47NgEYsbhqMjS$cU0GsgP0XS9_=Axpa2+)3FD`^lfgZalN
zruT#QPc--wB0l;L-((=9KSK2IKK#!8bD$^?$I2hAcS{&^piD}=t~1tOVt?z}+_`@P
z;Q~8E%ihlBO)#6v%F1w`2AokRKu}(nl|6ynCk`(D>GS7dhrn`O31L0H(nu;X7Vo|!
zb+Erz2n2iP%o%p<nXIEBGQQJaq9<YNTHu~m!2>pq+oB~wdP=6*q-Y%ILT0A at t=;dR
znwA6ak0QbYP>-^v$4-D>xv*FBR?M9m68j?{tU=Mh{~`_~`AI@*?mDp4tDny&jDd3U
z1EbV^w<5Rs<42>uJY8gmU%Yr>JKM?ff3`0if^FDy6T7mx*;3=V6La>SqLC5n at 8wZa
z9v&W0ucVMdD8 at 2wE2(no?7IMgxUAF at tTqtxjD-k|JeEyUet*Tm7vydTD5*&#bj~Mu
zG$gPc-qU07sB$s9IOgW&2m#~W<~c|>*=~TwM4%NTWWxkjU=>#DG$KWl|CPaTFyeov
zC`EsLy|mTgz3&T`=E5?*uoGr<nFu;xU^gklC~S*4<k{Q*s_&-Pa_5jHI(Fhj1HevF
zm)T2e3LJKj(49%wK)$26&*-&4&;IQUyVh-YSXh^>@jyf|yT1|+ayAX}Wz&pY%HvT|
zqGSO{7UoY#!--`)%#dcQ!Q$f})dHK1q+sJs+T#Wiajfk2?+vAihw3sz#v^7#kN^*`
z^82?8kW_{9a;Ry%Ye1A}*7)?aKe$ljkU>$*j#P^X+Kl8l6#Wl!3*0xYML}X|lv-+5
z;}`4hG*?$ubym6Bqv(YE`oOVMbJEq#Z36tjO at L%xw{^3ZQBfS&GymrB#5=jyM*!;+
zy!SO%A3GV(_L&n|VIC(Yo<@3)mzKz7z3XO_H3^VQlEzu9l&+_Fx|K#NWA$58kqY!v
zF%!6V#wW(hijh<R at a->oNP}EgTI at Bn#Hmj~5RiipXec)CDR7um22Q{1!Z1H*=zOUo
za(-bryP$v!rER at 0!NcLu8IQ|F8r(?Etzex;4V#~xq!qG`>%RMcDtRw^EpDI(0l8Q8
zt6gNnE$7X4TXl6cWGx91E$^7Z;*Q!<!uN%Z@`b!gUW;$+RU6;6WrYI&t>t8ZGarw*
z$y5(m(3yGfd|GS|`?e$fHHInqc+nuQy=}vd`Cc$M<=t&8tQPB3*{NdQ`yO!HhI8F%
zvX_14>yb8u0FZlo^(1xhjkzCWhjo*3*k)rOOO0|{7V*9HdpC{@)|Iqh?MoBfysUQe
z<VjiyFHsUE(Wek+lRXL&H%v^<wSM`cq^1 at C;N`Kq+5!eAzx%G$`tI&7@*HFEs|jRp
zA;jSSk`K}^^X#2h7>u6x-UhzNyIGpDp+!_n2q2<665AF4u_vg(@Fsa|%mAf}Muh`M
zd%Re-gZ;licKp2tH&P6Go*TU~<ek>s^h3h!Aoa$2)%BYJ*$hjx4mUd`{npvV^>qh_
zh3~{5tgZnb)Bh|Fu`-F!j)GuD><(Bo3E-nvapj*1u1tO(8y_Ee%FVEkpx!ea?6b@=
z?31^J+d(Jr0n7?K{7wWRDJ3WO_3qH-qUFcubihWq-QC?}jEp(|-6CixL1h&cKeb$~
z at yg|zMqE-+#pRalyyflIC at vtBCtA4yU1n985ZHGCC&0$n?yd!Q-(~q5A?JQT=rjYO
zJOOvmfTZ3JOnLRT<9vfj5$rjM%hHD#1mh?|zh!VJ-`+v*4`;bXFOYb_l$RDMPtg6>
zQo6=~;w4Q?-t`C~Y59`l9vK4zp^c4=pTEB$cuEk3?ooT0Dx4WAoB&T%Ypq;QgMxg(
zPJ;yW%dg$9X#E}XV05Foql46bro9=6RM_~$A=j%>b89>{X?t at RQ$lI-O|CbsmMIrn
zTU%Fv3{M#v8iFX#TI(N-bB~ISo?I*&l>vvGsFt0imfe-BEmXC$R9P@!+3m!oo`;92
zf=m at +?9>BY2xy(yYcamC5bU%#I1VA?r<d<MxFpj)FkDUTRCx+1vyON%Ksr?4N3mE>
zz}y6K#(MV3uqCs{3Jxn9+bWnmPAt`bNmtRf3wsSVy$R&?Nvptz%v*o~V0Ot)ogyw8
z(0Skvy05=q1J>c^@Q0$oz3-(5+QmtwlfEyP+~dIq0ct(ZR?q*wQ~2)=e_uoS4e3Hm
z-B~)c=yI^P*;Qn!07_UoSB;H><2Zw$rER5^)g$eaxnHip9!biX;W06={U_!MA)9W=
z$@$5qHC!VVJCmDlO=N1-BKn;GwgxF>ea{WmYUbdl8JQ1PWCzu<Pr{0C!2|HQu1y7q
z7O(qU22{w=uN|4IftzNO at M26#N|GO{7-~U*>b5 at 1x$~C)!HF6W`J7>YK#=fR50`Te
zaaAvGr}iy$<!D~oUGGt#<ukhO%XnCLxdj5()vV_ovDcqz8X`LW>vKQKRk6>*F1O*$
zE!E7o=QatJh;6>t%0&f)-#-T?ORIhHi5p6-hR`N}GM)e(F6_L-I9O(*zjJ=?pzPt=
z)MxOxQA0Z-&P&h1^q*#D|CG6xXK~fkep5>>RxtM?N$W at cUq9d)sTj}u_frvC{fCj)
zK|}!mh2h6;eJiOQ8n+#T%yt>~rzv>wg&IH-1CAFTgQyyn6Tl+`&Lnt`m`i+lb)gZg
zQm|gN+xW_OqxGh4^uEoX$%mK3Z_U-rz}gp^bsgcUUZ;rn+MaV0qyAUAUWFX)OuIva
znir=;oN_iN2P_4_OC}E{N9RqRdf3BrZ%8}=R3E6C#PV{D6Yp^Qr02uLx7i`uP?2+G
z#Wr2=sbH_;S7XoH3JNztPoPVFu%AJnqIB>3SX6Nslh_I0dhnq50_#a<Q61i4mMlk&
z3xO)?tAOCLsog6rZUgI!o^#gWDrDu<^z?X2n7H(V2WR*Hba+=_fx_Ka7xHTxj0#Wl
z at F;AP?Sn^%D}89WlSf)#Utc~MDw680_s?_9!*v6J35T6WJt0l&8AY6kU_<<REgVH=
z7u;s_?nGZ~21QCnLGh$FTfNbMx6Xjqpf^jk)1m}bI#cUilUZ--=;%aEKGi%k9Xz}?
z{iV>Nm$TTS_XMZ~U-|TFV2!{dbmi+ypza1pu$QH^5x+Q{^PA^;vvDw}*0A>L*SM{)
zB0wTBgP=gx>4UL)Uve_C$B41#-jV%hKCHkw90f;Xxib08>po~PRP+To=u=iE1a#qO
z9t-Sn1T*3~bBZ=Z#=N at MqB;3SFa`q~s$9~Nap#~+$Lb>i2X^!4__$B)?ljM>+qW|}
zjsLxTE*o4P2M33kq7y%FBOO&j=tJ=JJywsNAS49Zup6tx0b%{F^>@i!bf#5;LXk<^
z3YY?13~-HVzoj62f$u%QU_8K`DoYN%tq66^;Q?hDpp|PPK}O!?DByB- at g$d;!C<j@
zfmSZDs|MyksOS^Kc-%fmq2q!Y5rZJn+pCVK5RSpXMl|g#*M@<60VmvGU3O<^=g*&d
zOF{L4W!RI>T5oT2PkDI~BV%KLJ4G!m&RiSroje=L^YVTJmLDP48GgnbGMDUYK`Z+A
zMMZ^HLxO3s%DJ(+w^5OWR?t$TB<#b;2X&Cbb`wp<b2N+aNyXN~d&Gg8{BK8|L%v7Y
zZJm90Zx7U>fne<T%*@Phmq%;Ae=iOpOVNNtMy0b&`eM at uRUi>N at S+$Yr}iI9B?vG0
z`T0?msi~y}5htYi^-8D3tYUTZYXL-3kc_JU;Ozegh%L^5>FbH1N=i*_nk%h0c&wKn
z*f{?9^Zh4{;}{GT4b7dsOu)P8>1lQzp1{xdrS0ta#CLvPI&=QK at mQVD#=(8X5}tvc
z9!l6x)b|1WS3k=;lC92@`tF at kSMrS*5$B7K53-Z7AdDa$@9a1%-pPl+*j+85WBx|F
z9dFtm7X+%{%y|#?wt|0W0nQ8c^Zi at PG2a<I*waTVm9CIr9X<(aix^x|%d?YQyyo4h
z7G>FYYUOlyJY=5J`rM5bynVw6u*6(0PM931%wY(>t4yVjFKh#LSUyJBE?FRxg`8Zq
zcZ_8b)h2mrwpbmm>Rw}_t~>BF1M$AKvvX(R?m5aMM~<Y{|CZQY!96DzGbWgLx}1<5
z!p+6i^@G^E{u;i;${MlNCQ4L(Dz(NA8nhhBU*%Td55Lw{SwwG;-&#T<sa68?z_Hn$
zUpLyTudh_(<Pbr~%kQvrarw%m_|(=)$ep9qDzWg-$>9dHd-w6R0JcQv^l1#pkofp`
z`At<dHQ7Fpqe^;uUX3%BOM1amQ}^ZKMd_|wxgx&+A at K}AiaH`9qL(QpNo;G(cV&IO
ziBw`Y(c#{I`e)w1-oW-vIC=SZ(>bmCbrvPyc(3FZ9S#`CFMvzNhI)1_Y-D6)*lMY(
zso}@A{U~m?6l^pq6h5WSIAl_Edk4)G;QL%<B;mm5Lgee_=j8Z7qzqWf+_>Ri%!EO$
zV(p(j)@35q)KO7Us!V-~p-f`Wtg#iBKNVWc6FSdC>wDG5 at EMa)QwM+}gOyCO8Y=zN
z*48XE>fw8n*|T`ivoGyHdw*;2IjIGaX9seU at 9xH?;<Ws$RQoOg7BPytkkuaSyZbUK
z<f=8oO&ai?=H)FIT%7Obxz{J25EsV?Fr`U<GZP5@)vJKc&QAKUd(jXAi|r!I*GFqS
z3-5n^;U<AtVA^r-u615P0q at IKuOQ#m|EySWqe7U9n%V at abAEk&iJXRpKte(S_I_(?
zOJ%2gP&DCV`SLc>li}gv_!1WpltoY^r=&!@d6VUE$(pkdCpd5TXz{|Ax6UI$u&b8+
z1xX*nOGT3Jy?=)jVDxw at Bwqc4`vnkI;jdo at fu)}WW<(t?fTaS!B+)-pks4*zx^Lre
zdbYN;-GDoevc~^!E`_acrJ&2qfxc;mUfxOlhGu%tm7_YkjV<6Dvf_x!Nrfu28 at QK)
z9f)~`ECs=t?PYojoB`}kmx67HT3J~MU!w6bi;_L7dr+jpdD6qfBXx57&!5|yf8e(=
zPfySB at o^kdQc{!&#!6LHRbvYagv`v$E^cnJL+&mvrv(I525k%sPIGc{j*XAA5Fc^e
zJC&7@@wm;z7oq{^hmnNfxlGD>C2G<O#)2Wi2ag9PL}G7u;Y)^6ifzj&=dq?BN~F;M
zNW)`egUic>A!7^mLN#Mor9zUHube+mVA>Jy9~ejg_XuC2Vq$P#EPG6UwzlfNd?B<b
zp at -}M1?dOs_?yLLl&@dv>LfvDH-b8bbU^0U!9f6nMhV<<fUy8m0Zj??^+)mdn2-Kl
z&OKK?KE6+1zTB&z>&*&#u5D(96a;FtfiTNb&8Ekll-B(y`n_y;Sf}aZN5M}ZINxO9
zE8KRT>plwWdF#%dhXt=~br-RfG=Q-Y*2 at D$1eneW$A_4 at Kfh*xZ$UxyNaVeUj67H`
zjGeEsY5E*_o(~+sjnKP?C4)`~gq6AUga!j%$&@a3Y~0o3L`2s$HR&EbdPGJ^$(Ac&
zJ64x4xD4=tI`cp_sCk53Y>%;!_3$x_^68zfo6OB`E<Y?V>taPcAgjUROh*ya`2O}y
z1$Fp>f`TqvyBBq2!*=r8j!B~80wz2n!bi|*aI8<?yE<vFmjI*?y!~seki!2oKyUUt
z3TY?xBu%}`bQxMW6;)M?GAT+Gx=Kno)q2a at 6vGazyc{m>?jpqeDw!&1Tp||G<Spk{
zlrIoZJ`VyLaiV08f{+I31Zf49iG`O}(X9qp1x$&pFr#v_Is7Xq{W;S~5U=1fHMpta
z&Ha|840)U>YT4t9i^LF*u(P=3)K0%QHv=IminVg7w5U6D at nomNB5Kt@XZ4EXm#cBy
zl%wbHjtdJ5Ti*r=-CMykXuz8YnET+t1NI9S{Ppsqt)ru&{-Ha){F~<H9AJ at 951f_=
zAM_pMOYhcIixMsH4YI*%+5R#qQZ6pSM{se;|Cu<N+aD-0W#i(?mX`<%NL9{6#dOrZ
z02}nmi-f0Y<;i+cmb~tEt&Lx|X9pb}-RwZoc~Bp?!Of^9^8P*loac9=!aDc@^O%uw
z46Fwz6XnZWv8pAyehtk{xSUZZKn5`}F+p^{Y#3%GyZ8zMJH1vx$xj;IFq*ivL+R~~
zqW{E+M{YK3e>M7GP;l at yd3kx?lY2k=3)#Sd1#B$zjTRr at oFAOI<h-PVraVTyb}l@!
zw_okP!(&+g<fzNcTW1SVc@*I1!RteeqvlefAQ}1Pqr^<zOPxu$Xih0tyNr{%JN}CO
zIn*Ls!d6a(N$l4FIO1T~ZieDvAdJzZ6_S?m?n_qyIgDFEPr1xzrtcrd{KEY32YWO(
z5<txGeXQkhW=^>QP!CxjZv7fY5O&HHR~)-b9;&0hFRB|8FneHf)q*{Dej9GD&v{$a
z_K9r15w>GR6AuTQ2m8#JF_3};wz)-*!1i|wVvz1Z?URMReAJ*`S^L6c-1L<EX>hPF
z_`0+^(MZCVEdm4KzmtFaY`igWa-&bbB~i*3na;pQA5`lfsb<jk-n&e~C~SMLBO5hA
z!S++!zJ2>?Xs8SX3-V(aTl(4T;^tr)G{WZN<0JfN>KKHhfMRauU17<;ROKD|&`eX$
zeBkmr7wPfJfM>eCU)RQp9x7_-VFxaoag&~_#D9(T56 at TUs1PqFoDT|5051+Mn+yAj
zzzRqB=+S0z?GlS=6;Dm3!J~?4&vK5Dw3?S_Wgg)lV?5#{%m`HdI69i<tH~uWEI*w@
z03Sgyr*VF&D>>~3Hwxfv!2cQ?bz;w4>L|@>5xImmxin2JTl7WUEPGWNa=llzHLU$P
zVB04r*{bo^3f>>nvJjxpe3G9*c8ZK_Y+WmO at 3a1$)uQY>n2MUGed_5wNyWt~T15D`
zvUl#F3AbWBrn*;w?{#qx9!)q>@A(=XCn1rswB#0`4=HZP3Q9>ybuTVd>zNeMU42Ue
zUvAY*@H^_>qMGBU@>he~L!<iv@^7!A0S8Q8NL%LJQCCQv1=+b8)3@@0z67Kbxn(2L
z`Fho4OiZCBZEtRSdzYMT7{2;e-ffhukC2d1NlWV)xWJe;6RMLZ>n%#8^ZOdj1Y~>r
zZ_w)Oi>qm9xL}QgpRFaIC<m<)7}!Frnt0;Gi5DZWRn^sLo2U9V+5hZ>xp-4At4{1`
z>O9++P_7aRy0g0fw~zGg<*<E+m|~_%f1xo!0Ldw><(615 at +i0HE}4LiGFqSQLS+op
zq%0ftVtCk>m-|b6p?jvtxP`=HYdr9loE)mTfp`m55dVXRivD|>qhYD3sTTi;67!k%
zw?N8u5Qp;{!q>0geVe816ETi*2~~3A#N11zM4mTGFy9Pi3<A3*jHO{<2%c;SV-pad
z%CFhx1iezi8RiY2RcO*ik7+;nMz-U)sa!GA7xP;;VSmPZpWyT7&)~ew!zce4$ig4>
z+&l%V=?_==XGA9b*?c?C{@${;@$S~DYN_{B#7$H#RdtoSJHxTIlkdFrphSz$WyRnp
zwz6;03m6M^!_8MR>kaDsj#Czx_q?-6FqV|Wh;PrbiEhuP$S)*#{?_EyF3lNi37Wab
z47wG`nysHj9Tt`f*UBB{D6TrrPYLi2swygmQw@@kkOau2L|6y63QsuQx^)cb9Mz_}
z%*YrSxv?}<R4NZ1^Z#obC7Eu$A_$-MF?Ovhe^+p6OvnF&ctd+$RDv$ojZvaJmiw*=
zj~{NSGBG+4(|_^(oF`?H_=Aie1AJ6JI;vl>c+q8no|DQ)c-$>2*+y{H{=$U|H$b*P
zSf`Cfym&z|=jB_E$?kpD-QCTZWVU+gq=Yj>BPtZVzj(5=xkIw*A}cK|1qJJ#TvNy0
zDnZs5c0G~4#Nyo<Zm)m|)0u5IA=Nw_3-TOloCkldlIMJ%bC6${@@{;@T9DamktSZ2
zn0VSufZj~tjYYO*@X(sY_qETsc{f#hsI8*(qw^zq);5;Fb%1kv#jW|Is!G%^AYf!=
zL07wPVJNR1`JM- at RG2yjn<^iW82Zkmj*AC*U$#!C?Zij(=!br=pGo#U*ar#uws at qa
zg#dIDSO7Gn2e6xZmXlMqB44w}<mvP04%>0%15GpO^68Ny%o}di>rCb#*$OpMj}wEO
zU-x=gsDX+EKw8M&GxT*lbVed-dip5nNnriPFJJD^Gt5j+`@`aJ5G~Ek9S52Nx({xC
z0)GSFl>S*J?`GBc;7#S#*VM3LcOS7_Y<4eQa4#!=8Q5 at ROoxfddC&6v->M)vo6K_n
zP`sM`?FBc9S8XO}ma4#hM!syb@^{I%clz&2J;h~G$aO0mq`-%xE<1b+2J_cEvs}E0
z`e)JSG93B>218wQ6v9N;j>*c(W<AF}%gSo{%&&OEO$>=B)ZFh7+tl$rrS4%ix8e3R
zUJRu!ih`xeWpQrJ_jtg0__kJp6R5?Ercd&8K0Kkmpk=T&^*KhuyJnp<i-A~K=$&U@
z^skwY6!_*X&~T}mhW|`N5P&p9Gfk)l;JiG-=_?bZ*7K(sq*jRYh%3PXq#F6jlm0Et
z-i(Aa7hmF+jNH1l<2IvLS0eufsLH4}OBI#xK==-p*@VHf8Gg^vl<)OH1)H%Uo7(22
zYk_Du(#6Fk5hxOU6iR6L^}(vI{jJC<+|0!0sCP?mwmRzhfX*{OV=e1lH at hmGmt)=9
zomR%AJpN2aqxQ;0{Tej6<S_s3c;tD7k39+!xh@)-`F$x6MyRO|Y|l<CNEvktWPQ(L
z#gwjN)p|U{a+l~s*f}^N@`AHOWl{=1`dD~*dG+GgG&}OSsOWVK>5ggVs)?vR|Eo{l
z<qG1My4GpvZD=xyI_A!ElZt)0*1o1#B-&&NQCBvEBdl)pDzNA}`)?E5+^5|6p#x-w
zrmj(@eOaA(ET*pP74MuzTc=novU^YX){hBZ*Nxr5`WNKi^qan3CnzjjKB!o{^_Hww
zh&A;*=|a#b+iK-GuOE56wKg=Yk{3vof)fiL7HFvyIq9BHz>|`%`jLF3L{#S#BjZo1
zitlCu-5cTIWa*AVgUo8m%F0#+6%~#Xe5_;3t&l;7^Y&_PBzeIe)#Vp2?(OY$rQM-H
zB?j2Ms-ziJ8X@{_afey5?ca-pAu_&qqAy;lR`_1LT(R!m_rN3kg=dFT<tc2Xa|`fD
zAt**FYU<>~L<~4Cc6Rn~YP-sU0y6bHoqE`<w2ntoxoT{pqKsW#UCNI|`1vW?-dv7s
zz_NCpVqzL$ABk*oCUUJU*UJz5eE-q>HKEsX-JYPyPXvA0>2tjLmg+=)sBD_Fq9PeM
zuCbY!^VOs7Zf*^Ied-lCVDGm{42lNEAR^Y*9)adIZ=fj}AOel<TYuWK_Jwyx7I7E8
z9f!D3Q`&CD?xov0&z8?jyszM#$m?wk`desUS;((FoK*lNOioF8Z at Hytz!G^mG!B5q
zsaMw5(F`W9X*=U{#w!$(*zE_82RHtp9_+*KIa0M=d#DXMR|q*W+mlWT%Z-NDa<oda
zJ_>#_`D*M*e$)WfN$Y(H`iATvn~p-};*6yycr~E;HE=+QcVh)*1$^dx8pkD-l_`vj
zjnOpR-wvS)4``HU&z>RZg4~>J at F)D#+A8hjBnXZQS?;YZ7fWnKiBy(1S+A<0A<O=D
zUu|-6F;!+}rjVyJ4xhn at qwSk|16%sXYWK#5E_3Uh;&T~xw9Q^Pt=8A<IQGz(cI=<=
zIThw9LnCt8R|IdTS?-gQMG>S<tG-%b8~drnvE31_e*INo(DXQyYt(($3{578?R(Rz
zp3^=LTqQM`QeUZSz6#9Hz1EX=GBMLjgZmZ3d~`U$iq5s~eQaLL5az4ysX!KzgCWD-
zm3ZSeFSiChWKVEYljpn&%(_vaCH}!Tdk{aPnRPpRYQaY4s1pztC?SwRN#*7Ji>%vu
z!NI|A^$485SvZzR%@6wJPDct4XPgpoLh~tLTkYSB74f|m_q_TFIAbQkn`yavGrqe`
z>05W&ox=N{DdNOEE7~dXk+h<sx|Oup&>*c-?RFBvG@=1JQS;=<6V6B<a7qF;BaBK)
zN at gQfSCUgw(uTlKn|;rrGcq!Iqf=taH}`z2iag=YxyajZzFqSOcrABsPJET_?71DX
z1e>*G{iUbNXN)LROSD2D{eYpb-0eqxPeDNe9Zc!zxu+#6Dw^7n+}bJ&p3wXVMRqUe
zDSe$*-!Z}1yW4r}2kjaRYn)?i^W)y{o^5Sp1ycSqhp^`|=0gAyqVUnQY+9`JWz;OS
z6lD0Gr<;xHV)SCo(t{W at ssRFv8uSrFwqh2YLnK&KBk&Vje`XT?wh{ieN*#2K>fTgN
zuT6hJb5 at Y3N-8R6h{NsF`S8jGSH5?2km^=C-GFFn{c>25Utb(5^GP_Vw1K(_flO}4
z>Pn7(bK96tvkkUrDM>C{sI%zvDalg}ZDQqo|6y%oBbWbaX=##8hzy%p0B+i0vRO;4
zooHXHD?E1bOSagT#?P!P-1&MMLv_Vf%Ih1Uu544|My>`Q`0(PURTFiy#qaf~M~@XR
z;Mt_5rFBm>Tk*L*7Qw;6QI65d?v*XOp=wO7V^6FU%$eBhpF>wuYnPnR;%G-x`byn}
zI=HeRp{l#+;aSr at 1IN3nc{YMzh+xN2I29BW$aW55D<Hb&eo4xwbfF56RzpJrs^oQc
zDyGkzqM%SdYk6U$B}&k1*X6#63G0<Be*(Dt`=Y^9p>c>$U%p&(bGu4GLGjZ1g%QcA
ztA44eXR1~|GN8tM9H&noDn5YFNw>g)YnQ)cZG92=c4FNtzsoGO(+NxkI;X<Gz=fqY
zv|bgPRGYdQk at fk0*1e;)O4HgZnEbg*svbg=%df$XDJOi8NwL$S0kxq^3J&^7_x}Dq
zvP)rMVFKjgTY20Uo1G^?=B00dkJuK!2!AxR;~@194ti#BO>b4?^l8a$60Vx%XUiX1
zSXjC at RSR>stWVcm3brkGStf=n)-Zpmz4K3;VM+M;`O%St2VY(xpDHU9IE+LtY6r(%
zb<FCL&$)H|TMRpW_eBnl`Z^z6)JTOK{PsnnG-JGKz55OtC<Fa`?#$V<9RJLCRJ1{)
zmq{(a@|zdLdT_9_OM(H7sw>IBjM)mGme8e$!h_{NXLy>Ln^6%JO`BbH`uQ{_hSt23
z&-TLZ-fT*k(a!HhG}Xxmw?fi+=7uUn_Uc5l3vfPC@@SY4UdN6dlLW8cm2{1usih?a
ze0%reJ2kshLGYAF5aZM=HDH&rI6ef_WM{9i;J?D&x at _}jO#hJ6A1V0pT#wPji<y=(
zFKpXr__Eg8E<_QN_i#f(J-MPt^f5Q-<=~*iEJC?p-VD(xZt2r&EAK_gE<8E2mRMA2
zOwk)PU>Ts7A5CrNwOAqVHH1B5d6~R^?(t*H9HZ7SwU4%)c6Ki-FRx++L>TMQ>}IQq
zKi7{HRNPq4Ehp4iaJbrUS<1OX#qzLkzMa+9qk7sz#VwDxi)PV{;~XX`pAyGXeXYRJ
z#ijh-%Ag}T%g at fvP8rJL{Yzj0ZTobc(NLVk!R{sS&QaDB7~ze%QSXDj{$lgUC%H;$
zGBWtEp}tK`*U at M(swPTHAE|I$_)*Nx=PG;S#!;jQcXvIee*9pSkYK)Y<x2N-6qkDH
zoj*IzwQ^<T<esYpX{*dS<$h~BZkWdx+r~>v#LmTa?e^^;2kc>APcpsdN-kx>&YEKh
zu?GPGfr$Ty8xj>7=cUJ%w)>hVAF|f``oiVK04n;~7~tpC1hVLfiWuSBcn!^_8k(A~
zi*x&zH_nAbP|gsnbvirN+vchf0&wCH5Zp;xe)D8}B2L=N`k#VCYN~?79u-Jl#!_qS
zGbvwuh_x33ApOvsKB>emnS$u5-{E`;1r at P5uB4tG6Dod;jTz)<myr|}7Rm|+T0h2G
zxBMI^S_N#JL}dIaf(_Q~&{Eo=6JpO#<L7=L)cS(kXLPzWj at FL@pGSm|1PH`@#gB}N
z%5Qeotk at b$e?hzSI%M?3!~`mD>efcodw>#f0s7(f-uFN~<O3k&&T98f25-Zs)}37`
zH_><$qp+>y8-YYnN~Rt0&95(+qA^U=A4Y#{4yG|I8%k2n>`cBv=rG%f13C8c$ndGF
z4w5=L!&VY=k3lS*upKp<9Xdt+E?nK30yk_LH#}SWe8Z{31rmxp#`TTrS&in<H>$e4
z;06Ea>8Q6hXLv8<yPQ9g;CNh)@-j860g{oAOF%}72fY at i`M$7W-B^OjQaS%h-<c*Z
zFSFuuuSC&+j`1fgz2-q($A^4{{&8hx&QY at 8ONccdmT27=xs<DxO;u3yX!_YbiIUw4
zQIAFXIzl3%>n<+B2;l%wfpO;eLH)t1$}d<)6*%g?eD!K#wkrjdnL-(_Ji#R(Nd)ak
zD!PKH*`CjqS#<*)hq@`q^A#E$eIsO(=MYI{H{Ng*bvI3l=wSQIhDt90V=L<Eg at QO8
z-&&mn;fcY(c4(cj+??&gt#;dxzJEWpp=0bo=fk6;!17WcnEswG<7m1->KGj4@%Z8B
z at syMl@>8cmzzUrf6olW4CI5FAF(ZThXo6}F8>iEkM(i4|GYa+UoE*7%-)Jy<qwbD6
zxWjA9tP|p{%vHPMh5IIIWdMs2=blrr*xFg2Ip^Iy=U^+$*zL^aIy<DxtD}PNjMcY#
zYES1}Z)+#d_GFy(7!wuMnYV05j~=}(WosA8*6uAWEuA{{&q*_-yYZgCHFGoyk1X`%
zw_u$|a~<rD)h%Hw`P|mcMEAEQHQwJ3It+4tDpCk?H8pCPFs2a5;oTXD1Efn|T2u5Y
z1Basl-Ip(^(D8~CnGofF=KH=K8?Y2KAA3XFf3DUOyqCGHwv-eO2+cUAeC13a!ym<X
zczAU^J(Pf}-I7P&$_GAqa%5_1s?Mkog8`5MvFr<0HAyvFO<w9}f4 at et<-q$9g6*_-
z?<QWG#@CNG2CnuShmD5_jYv8;I8-x59xE5*8+>w<aHUOXG!cBHMyVy8+wY&6lg6NB
z(9yM?bOofimBV)+F}p|KY305;GK7w0$;n;s%hN?;vT8cnY)Y?(9PNlbzi`O~Eu1}j
zR{5yJ6;jeL&icg)zbcy_b(avBZ~tn#V)NiZn#uz;6=Qp1(a{{MsifHo6)(yuDHC!%
z>XV~QiTi_=Y|Q=M)mBqdW`)FEiw$N1$)GmPBM35deZQ~mth-eY8C<Ykw7Kn<1%Ckm
zYu(s4Q7YU~g+1?j at 5w*?V=&~YqXf=<y4l(Q;brv~m2!J at PXdO2U!vx-^M8R3E^jCN
z`t?gmQSmX7nNmN&%5YNpf;?0`uKor0yP=^Ws$OWFKSz at GkO<%R-sv5{B4n81d&%hN
zf|$hI5^shvJq-(M0W%viQtgfaYXXlrL{e7v1nLz3UK}$1@%43ot+xb9D&W0(qe97C
zwG{WwrP=;MwqmUdz<^*F&>^ke&Dw*b7<7IYKuHFy4eG$}PV;!1uS;mJdZ7juh?h+1
zO|b6aZ{LPM%1q77+!@;0-j+RZeij^T&+vnUm{@g%4qMT*$=G*X8GAQ})B4MD&%#D8
ze(VieP4W8Ym1AYQ;(t=lfYW at lp2-qqFYL;z6Swd|4LAQTZ&hmQ^$LYJk!QzC^KSap
zmuOw6s;*AzGP59ZdniC}{)C2{I^Uexi2uSrUHvzVBm_*ZQ{*|imDFIl44G~lKJ3eD
zZVIC0vmO at q*xTp>kEVjc`mRjMF>~C$eY;(VJj1>14K!`|qy6m=yGe7}JUWA+AhtmT
zA696My?W-{InICPJS|R7Pfsg<(jqdT^zFYst3~9(1v16XuCQmax~eJ;zxc+D8&`-Q
z!DrLK-WePoYJZlT%!*E`Ub;j{M at RSR>(`f0f|!_?z&L!Ms-rfvEG#zbN>+-~1_{25
zrH%b}7QhJXFB;N?W#seRcKG_{G9fUCN`<)tVQXt^1Gp7*zJ*mlfGU{!LPH~vSvr9X
z&B>FueXI&o)6!Vj*l_>q9K_#Fi_d`3IjX=*dqu|6eW38o$Rf1sbKu~#Dp$_#EjeLd
zsZGPkm{XUHi1V9mlfl2WKjr+sJE~us#+;Sf<cOZo^d>b+I{&HR<l;)>w9TuZtDrD6
zQEHY`33j4+me(f8x}p<nH5HW}l(B>Yw6g*fsO;7tc4Dbo8ZSo|_ZzFh;zf-Md6x`w
zMOQy4-uqNhv6H>yd5oC&#;se&q_15gr>ECSt8jE=S@@`Rw8v7A4KxO5W91bsIebo1
zqhK!!*YL0Z3|@3ooIE-GO?}Z#-<UK~Q1J56_iU`Jsgh3U<ZqFw0xWC8+^V^`Ijx{2
zEx=^~SY^~9PcoJM63x?M-ys&raxK7Mx;1KLb2ECWXlU`4f<m;{N3g at fb`yBq)PzTl
z96?8jVf&?1x&X~i!xqB7f~xlo3?!JCoJ<>Y0`v at ziRquh--iI^DDa%?KC4sXK{qfk
zQ10>PJ=Kp_2i|hIY9e=8e_s5G)flz6aa7ALX~oOR!?CeTSZK;#D<~83^76XuHKJ!T
z5&6->>ukGE^A#I&^GuaR?Ksjez9SPlnsE&OiavA<5&n&ex|T@^M<=f6JJtFt^*>JP
z96CR*;kDr6;$AsU`_HNR?3|n?kO>8r1N>F{+Y6|ydfwQ=fFuwX#Y=X6%`?p+lW|~+
z=>vkNPv<kb{EB at ZGUwfS#a-XvPlyg6pfcGkwz90djg<!CI_C02YPB2<pFn1>!xI^%
z?q?6jo^ycOE806cqVe-SU+Xu*qGHZ7?I&erWmE;!EDy;mLZ3gs4je1+39|GEU|7M>
z;wOk;e?pqn8+ZkmE?r8iC*q7c(bd&ujoXio;Ogk;L}qPU2*#pA48{ZRA0X at LODg_!
zpvd&saG>?!IqvGsk;V>*gXh2}C^sNoU-pcB*ftQLx10aQfrb%b>%s3gu1>ZnX=oh7
zAa}l0K1qU(dIbdqq4_j$FpA5I$qScgiTa3L)yvuqcvS`xGW0b1=CKnmRNtXFoQq8u
z8$R2i%S2X()w%_$oSKmui)(%{%?7r7pS1B}n#XMUIz`yokHyEwE1!P<Zp^`|l!bMA
zZe#nZe@?Se(LmPEJ9rig6#^xRL%FTiWhogMx3nx6im54`KS5+)fj2oiFX$}W5JhTy
z4`>_@=hUA0 at nd4XHyaI!AZfiY at xHkD+gu@<uH5T{b?V+gO`**of9p_4QPk<)*<5Z}
zu006-_U)Sm?f^Pp42ziiI~GtG?H4*ABC=fN3vBQP_Zowuk`j5m$J%Wqa)143YkZhr
zt(M(!GPdp9(XxB@?x7zM5GQPRL at oOyeF#3fSi1b%#4a<mwm-horM`P7o!@sYr7Kat
z)~oziyU5|e>m at 7$nrU0HT!;p~Q&Uq5F<g58p5NR(S1q`|zn{KwIOIyPdnF}~O;_MW
zYUhQig&Vb%m4<OOH8s|aLq-#C=a(q^Z2S)zX^HU78?>CIZ20KjRJE4H(B6%k{IWTI
zQPN9{_1MKH*ogx=ixPVD(*ZU%{AE%iwlPn9ebdA-|NCJUkgR=uWvw-bg}Y<OX|7BJ
z^CZLFqEk<x*s>iWtqG%f^gq7jxQEVUaO+jkUNZTrc*qRODJRScX?XOSC>2C~&@B4=
zw at hFM!>HHxa|g!D?TyKz9eY0B1DkI(W9aa&-&ay3dzpBe)0#H<wsE(P=3wz{`;xnl
z22;hvta{?@2jc{;SXsO&HSZCydhzbu<&aE13!S>7G*nWG;dk%H`fF<7(*_s at y#BK{
zzU<om^|1`)1C6B)$LhYXRbPoZGCu8yFaN4u&NPK&*j~s#Jkeuda2gHkfQV}DX=L>y
zlRm~0S)jpP6?>Ubw<x^Ce)f$g)>A6BwpM~vGx$1djcwZ%hqfbq(n%|CXu@*UxUPzb
zxE|YCoBp!vWomj}--RA@?bOu$uL%;~(#J$tSPazyet&FfSw-jl(3zOKcZpHncx+_r
zH}7qZD%=SFba!rfxkX}sl@!fS3)}x7Ld7)Gcvt+PQmbDs54=NUe7CpVqH_xi$w4@;
ztgcEudc at Cv>C&H4hdJf>ixR%i!@~51zV2zVEPrkwfW5p}zsPW$si~>yG(UeguJ;l2
zRCYqb!m|`l1Zjy11}tgm=_^cV-&{y`BE2^H$sNm19T58T>Ep68AutesE at +$W-FVX5
zzD!3>ZpU!ocbw&3n{`<w)usDKiQL`Y^BGD at 2P`ixPcAO*eQ5knB^xLHT!xKQYhlC9
zYgEh++t=4JqlcZ*#W{WE%+w{{?|giGlr_{$OiMlro)6bIH-E|ZahqX#dT>KzCMiBS
zExUj(KT at bc1YZ%i at Pe)%Nm4u(biKj1&vI0bbLvVYF)8UJ2vcFtZ2=%kB~8uGxTTQl
zKpd5GlD|v(Ed{H#CW5yHI=liPzbDX%ffr}*Sa+RgiA2Y2b{5N4Cz%gu(Gfq?Do##L
z=2?%2Y$PTo&dADY_+USysH*D!@uLh}sj0cy5FirGaW{@XmQTO-;KA97%1Tsl0X>GK
zJh%pp50B0^8LeK}PVEV<H+Za^S!EqFWGVP-hR|P{Y09ZK{#8uOt(h~IV(epfik at KG
zhwADu5`j)LjB8F3rCT{eAJ!{-ElMizstNG%U1HqL%#Ilw8`Cl|1$3EBN!h9mha9cF
zDI>!xD*9EVC|2&={zeDCCc$!Nr{c(*`}?{wcj34_U4`Fzhq3DSmCDi45glYo>a|$i
z{narUbUuD%YrAZ~!o!2nrfL=6Yb`QCk~Lxsg6*b>$-DHx8^>5KeJ>_5$5lVS at BFjh
zCt-g*eRgiHUa0oZ-QBGgi2z0R_4UplajUC#=#UCJA=ouE6bxub58i+HdCf67_*^o6
z%8-x{sr<f_^z=Z2+<}S9r5PcPbP&qUu2zzg7*gHfW+SEoClW)Ez&x9CKTQfX0BreK
z{IK7 at 2jt~l95Lqx>xY`k^77~m*w!AHoGZ+-BV{(sXfhXZ4h9_upUBqV;}o<S6yU<0
zp9*GRU})Um+gWWFSw=rL0~A?Zbj|wNLu2;gQCfPSv#ww)v6YpTXmMMGwX=gmopl)-
ze9(svAJ9*nk(VcVWN)9zKhOO at fL@}=c(ddP>5?ut$BHC8l1HhJ4-QkfAbrTiQ|ZH-
zh5nzoRm6mah3CX)LREwY9}ISMkOFH^Hgx9rzI3PE{)Lx~9StUvjwvkUV~T&^qexxj
zL}DOfvlQJkz&I2VZ?9;WIoD!cra+(LG~(v&e%`~=((>!Hr-z3J8MwHZ7}|a3#j56y
zALX|<2G%P--{+UgQ~_}$nWPA)E19X1oSseqTn+$jQ8IW|XLe?WAW89QdATrCO=xg%
zY=Hu6#MfsRD=z7}yNi(CPq^xM0%97{8y%*9c9JVRAt4Mh0#bT1;OYf$rrzFObOKjN
zS2yH*yvq at Zoq-NA1_pQSnm!#TU?OxzZMWkOfMU5-Z7rm<g at wgoc|^>eT2)nbc4;Xf
zI+{lL-o1#32p@>X7`b!BrKQO=HB7MLb8~YVl};BxUl+P<%wOsCR7>w!#Rw7FJ3Bfi
z6%|pwefzf4qGb1jl9CeT3D$d0-K`M at S(h!rk^?3vl#h;%N{kB=Y}GdKw~sG&oN&5%
z^JY)s@|phK<FWdD^dWsj9<gmdj*qWIM36rZ30cjrtnD*B4ocU+V2s!imz0zg9b5-x
z>LMvBEKCcZQx&6o|Nfb_4~J<IKJ at WE|G31#FNxL4(LEBR+Q7Y`hK~a2L$xbTKWWjx
zFFeo0=>Fp9%CCe2MgY=yCThqqvmU|JHVtlSRBPU9=JE2Wua^?tU1>y%32QMvm(=FA
zx3}l=t4(M%P+}KHOiavWceQ0TGGQ0b&dx5Li5}8L`cn>{px_UR0+YAbHCh(p<C$hR
z+{^^%iHL~Gs{{oEUh?P}pSp&31V{hd#|r)6>&ZMi1~XbxKS5omWM>CE6HuJ+j|?b?
zjEtOnvU^}!sG+E?-a^BKPEi8);$whtm^Vk<dFB at u?0|&PVW7#6A^q<k5Te|fY5Zxg
z>+LP^dwDbr>?isG4CwrLxg8sm+bj#3Pr2lk{0)qFULMKi%a=3q@?;zxFKcOQ8<q{R
zPz0UBE1Mg3ux6mApYX9NHQ1LC3~uHhuO6<9zDu29I|6ro;r<yvvPWMA7M+MlNE+_*
zulkoRZ4Mae1UFA~V?VnxFUTx1elpn+C)CzqIC--9T1VufG0wuLPq(5yL9kOk>4gnj
zSzCK_T#ZutQ?B6Et5*$~?xu9fX^q*sxb#n#fUv1 at Vc|0Qv{dvYU}?u%{aA#DS9_8h
zb-{Owl8a$)MRY1VijEx6NE?k?P&YY?Gdp-OGBAi_&xim=7i%p#`u0I4`TF|qUIro{
z3vLeoe4i}nI{c1iyHq&+ue17SaR0C$xbOB}D_7WiUlehTl9Eqrt1Rr&--C9aIy!C|
z8nS?J=bJtiq8T_d6YowUlR`ea`$K5;XHiG{V0-Jmxo?^GT;|fEQGb%^<jL_Ubv-FH
zweK&(u4VToXJxfM0D+90q>K#yeSW&1P9hZ*6}I4XFp^GAf{;K;Iy%o$+6oK1w6=)p
z#<ZC*^~YQ8<d8WlD^rXf!NnEZy8SdAvM;*xcgXPjou#U(syw=>8ntqFLF%H{vP-9`
z^81KA?%&5VDf;sUTQ}ep`Txjz4{)yAH*8!YdnPNJBxEIIW+X{bd1UV-J9|a4vnv%6
z(niQ0$)?9nLiQ-h9)*bib?N#2-{W}S<8ai0`hGv(`@XL8I?wyQ#s|si>jlT8diS%k
z-cC<XKchNaQ&U4qPCm}PFgj|EnRk4AL}2e*Kk?__RNme)scv-WIJ5*O&z_~I+8kOy
zqY*fAq$Vz4m6w5$(brf1nBl2Y!~siDW}~Au!T2lZz)d5*!-xHR^&vh8X>G<!SxF=<
zB3667-(*itPS%0XhRJZxo;_P!Sy@?Kv`H*4m)%;53#=d6+4frB+?>d7;;6f%tq=+d
zTbm{Zo!44jETye?&NZjYHobcqsk0cfu_+iMInV^AtbE#H4ZiWHKK;W9M(_IS$6NqE
z)1N&fZfk3^b#nR^G&?`<#8J-})$rGSuGReE!+Rn6<~KPx4SjvgPck#}{Wn}Oh43$}
zP}S0+3^dW!roqZxtit~K^(*tigR08Pdo<6U&9|uAXKig=Juom3oEAOk<XMqQ@(Y_i
zVCNw&E`piQIjYyL9VvF`Crr?ooVFO&tE;O+gIahE*cn`0EG5H6t-*JPtj5uB@~mQ1
zm#w>jh9|G`LRFRGgIKfb9<C<qp)MlAnh>3m6?KyeGH&Z{6{ZotqXyHmtr82;)47J1
znny;`M7^mfDPik~7UbK?xz82Hg}&~uy}Wh<Rz4NL%C^W*AW>5>?s<>h;iI14hfjz|
zNVwL at vsNsyH8YYD>C!s0N+fBC47H~Qf6ksL^DA+y7 at A+^7#ka_y!p&c+Mcn{X3D8O
zD7}n9<5-ebB}LZ7S885f(lizYZ~A0YW4#jAmY2svl?_G>c at j<vCh~}gP)j65?_?(=
zHCX1SzkW>)i-tuguV23wla(b8I(c(z%O8(bh4ivdIyuc39-+M^$+`r*mk!$(?Ay0*
z at q1?6IDoSVER%vlAxnc(36`Wbhz4$))z#I-^J=BvU2%CnD>h&lZZd3GH$mf2Fe3zH
zP9=jDv{!Ik9F4Bv0dOc<T3UQV1}3HnOxV}cV{B_UQI<qoqQ;{PZuG4qZGnFHyr$-p
zT2kwa(g){lO8D<ytMv;F4K?w4lgi#Cw~!+8KGP<vtLs8^Y;3K$m^t_puy2T3<HSTU
z>bzlLVeq;BRULL1wl_DwNm4~ZLSkYXsE{oH|B`QEm>p1V at 5ns&LSCL25i#*nyqAlM
z7-*}Fnf at EQekE~?UD2NzM7KO<qhceQrob%{DW^dV`@_!hLrzUK!6evaWm%~X8Jd8I
z$gkB-a0b03ac;-PF&9io#?(D*KLEmPFmUG&(!dKkIw$wfrd<LtGVHgYu-o3meVYDb
zclXk2xAugP*#<cTNqGhN-G_)fw(D7K<twYJtEKKVd)}C8J<e_^0w|19ofseIqK|w&
z$@8|~LH+F6sI$x|3LGpfdk9cjC at 7?c=U=*b(VI4falz~$MGprMwgU$a;3cVPZg^T6
z=i9e$;~zX|QthW;6fPZGVY>Sv1OOiW3G{V1n3KD^B>CSKL;#Z(AMPBIT#~8lcaSnJ
zb=lJt$2qkV23Em!_XF<uphvF>EWzWIsB at J2_O_=fbyPU^e)^RD?j1V;EhlG3 at WAZV
zqr$?ib-erqHO<YWzkmPk?Cm9aXWFrs&>?NJ7rtL#&~0pKnJM4nv=(|rSYvc%W+p at X
z9FSqo84gJa3Bur-YPoZ9(b49xog>mf0x8brGQHdu&KA-Ej&W{?hMe5h!23r)8m)>A
z#pl$^uXLA1-pQu6f4v`?pK2oH-A2wv9;#xxF2==Gt(q2FS}-uTU)FfWUG~9>;M!u5
zpMDc~Z5rnZFFCJy at 1qRlCgrIeU#X)Rqlh6gX+CpRp)~^H2duB8<bjeBa2PT&GI{?w
zj>_1$xNr@=C$$>|A#FCQj*drZuB6HP(DU;0V&k+gMfMkTbwj||pFVx+?fWwAnu7zS
z^ZP$i0U$9mV12-*y=U{Au-R7I#zyzd^!$9I+4eG1<)2>zIsy!Rh1d26tk-d3y4Yim
zFH|h-?5801V-vgX7uqXMfJ;^JEngvG-PFrE1sno`)_mp4`Fr>8Th4tcaw~876~oNJ
zqU`5a(c<~_-vfmuC7A^UcbOO|sHq=IR$`etA3y)c>&#_GK<3Ej&B;&A%XcXDf2$fi
zE+f--#dPSMM%<>nzk`FY$-?7(Vd3H85dFC)9mSZv#U6!j^jAJ|YaaOX6a4ez==)=9
zyN>i<;y`>nEe9nH#4B4{fxy{qk6#Z1N_Od<R?hyz=SP9cR%fV&V}))LhaaWc=9gZs
z1PytG!fR)8^d at ox{|Id_KX{*>l|_N=Na6ywtQZ&=ynSskc(s+r7auD!j7gRA8h^Jo
ztO`p)Pfzax3Ml>QQ=+WPxsSV<%$(J3+)w}>8IEbWiHV88E7su^qe(dZX<P0eh4_X}
zrs7lqS_%O9Wse=WnbvpLxxv(?cgb$v-s>I}#&V+cKR?|{2O=yfDVhH26&VIDJ3AE!
zy8vY7B?G8FAsXU2w1NWrkt0X8s at zUrb#-*OU<C0O^N8;r*0wY^C+O<x5)crm{?U_#
z9f_WueK7pdNgiHJo+x1T`vrJ+HFb3!QBfLrw29QvD-!)jHy=-az9g6keD53kla5Cx
zf`Qs<JiEwIWmaT&z$2(^5Aa&~yf&wb^77G%iQwpH%E_sziCDS$hvHFMA7;&^QcM_Q
zPmt{AA6WJM^K|Sz0MP at dK!=PZ9Rc_Lj#_dECmidAA+*a4;MbH?R2H_j5#cWYD#bzF
zau#qE`Q`ZnutJ3N&;Qok+<et at GBP@PGD at fpHkgjChRH5qIQ^&bkLoj|+;^vEW`Z at J
zja%r%sMl32zt(+CHsrHs at ky94tze?2iPIGc7UJ9&Nf1Uk3V{@Y+>p0zyKN|&wo at v4
zIs#rb!*0qcVtz_`wlci^`gYuqQ at DEOSZeN2aG!~ZNvEykR);*qZ;2#I_;M{{TUuH|
zdI-*EEE2B8pXsz*@ThLA8kW^%vYC$UgewvDS*bC<slitZPKb%qcvZ48)v>m!qa<B1
z6w7-$7TcH9&kt45F*8Rv4it}_HTz8u9B+D!1Pc>(`|<cM?uq44AkWv2KFxW9!EEPu
z at 72{2h3=l7hP?yrL&d79s#tLpTtg5T7>E^&MkP+CfMA%&m7VddU>8jM_fY9v<@8Tq
z0zAKYdhv+p at 7c<u`gu3W1>%ir$6So1t8==i`G=Gg73Y@~_w3n|W}uPiIQvEEczF_w
ze)E8}M2)$a7O78)7C$8oO+`qhL4rEty3NYn*`*?HKR at 2T$ISAd6#6F?7hM!-rneq?
zY*T~W0-tL;N{`+)nEv^*UggHO6f6bl92tqbbBEqxpqR?2*db}9A(v#|K5TD;-5UY<
zPXoK+xUqU+SF-1s2fYYCP0C&HC7Sx0oV}A1_GpMu at cWhi{P{lMqS$~CTLJ6qM-&zo
zo*21e^rCxg^21$hmx)>is2vt>gGZwF`J8RAmy?rIo8~Tpz$8V1VNU0?dnGT85z&u;
ztR6cPMly9ZHRF<&qLW;s>^{%CiQj_PVLP~(n3%2NjEp_3G5e}}&JSO^cFq6nxpSus
z4Otn5^-qSzINj;7LsX3AREkApsgK-`2R1qaY9I&xOB0Najy^JHhsPV(OmlU*`{k=w
z%2rmqjZIB-OiWFC2a=MKz{D}v(Bn#~<;27Uwj*X`W$o<jtd=`jK+Q?PBvOw}(kvUu
zvki;LKYaM`m}5B`2(ONTK_t-2jJI#;L7f2?4L-?rG9W+^ZxAXg<tM*>?HU)8aMaKF
zY`?G6pgG9VFo*HRjT<2GL>6kwTl1^;&PX&#1)UVjXkq(f$`#^I=P<@&G$<n}83GIN
zph@^7pzV4<aX#@K)C?nae@{B@?#t at 8Do#)E$hR<Y?(5Bb@?`zSkNaw*+|nK9VvA0d
zy+1z60S8LsWM`*}R3nU3yH#2$6EMS8#Tco^@Hl0k7XN-<KR=&p!`%b!#m(&WLw>Vt
zE=#EM^b1t(w at -1-kJ-!aluD(v?Mu`QZErvC?&%49yzZ-Bj$WNz5g~!UzdsjaR6=~b
zOB+|QzVPD}d%kZ^fJ3AEosQ$c2A>%{c9;eY3<|xYp`k&)&^F;kxR255+S&v*(RgE0
zfsF at 4LPF9D3mL$D8o*uY4j!DD=02a)*r<-hpqMS<^5fGH(9L5&80+`a$lq*Bk?#0W
zq?|qQE9uzP^{u?~FM`3tKy8^9^vB=37lQpn(Onbn-~Tqv-Qf+MDv~h=4_9 at BfxK_d
z)7!^Kng8yEn<?I{qobo9a_$vO26J<BU)d*<Ky}qOHUepGzw#zEhu6HRZkG9ByN69&
zMg})?jJhfF$3NmJBa*OxE2-^q{VU<&;kg at laAd49;LzTEgZiEVH5JuYx2Llk?Qzp1
zTo`3+<f0mGA);Uj`u3kqi-LZXTYmFpBnPFPLX1V$R%u@#hfgB`Z5s?!1Uzf;t7sq#
zHbvdq+LBjLQz1>-Z*xv#Ajvcrn<!yP;rQZM9WyiY&68Z!|0W7u08hkP_@(g%Qflg7
zt2)jHQ`*A7{dNEOXAHao``;#~V8wYjFcOF1N|qF9XZ784M@@`{u65aZGsjhX4Rh{?
zza6UU6&Dx3>p5;w;*jv&o!nI0=B?N-0NBp%?!BXS%zk6Fp}3hXbH+&y<?L+KBtL%q
z(9zQi!z(LrFr*#E#%wg!x!^6+)6?B{HJKl*Re&Ka2pXH1#K2==3E}4EO6>IP$Qeyd
z@;i6#yd>OWvX*4+8Xk_lf1gqE`0=H*F8GHanbv>*#-2tePMpBIB5=g`#KcokanB=q
z>>4&!rY?XJVyXnZvCb>q#kIB5PEI0NHE`<|A at Bw+9-i9nx<iv;No_U{MvL9zQ-}ro
z#wChJoJuMxPrJLXK4qWbpZUao at Zj0^qNGX0 at M(a_F6hp&J-69oKQ&t9SDk#L&0NfQ
z#V_yFEeM>X6cnl;CCsdu85tYje;Yo-FDx9bWTR|f?xID?UDCV0E^|-w5KWk;r|0rw
zkxIr%FRzlb*VCJuH6VfyM_%T?yOb_4pp=qZciu}XC5Fk at l}2A~<m$>xAdVurG-PB!
zAe)<ZvLg;rq~x8yHM`_M{d>rcI()B!f`YvJ99!f3_wP4VGDf*)L36gINYg|{Mrvqj
zO)6PIaD*@V$PmH>qUxTX;@DDPtS<aIkPLS(FRW+-&WXo?pWm27WoKufSSxtlJ&jGF
z{8zh9W6NP-Mha}7H$HBOK8sC^EiEnO6<RT1rRU^O9X|i48eAJIo*qBuh-k0`wz>Rf
z+^x*k8HCM$M>D}BG*5tOnw^f0jI2pMZol~Hl6J<+m(6v&sW%!M8#@7sTONp3gp at c-
z+PxpQ{a-FXb?T!>T1|=dR?+IbC=0|TQ-XV^-9?%{IZzKPd}8O|XjdI at 2pHOsNmj6F
z8g!fU`f2=)<BJqCrTiyHN!G(RS|2O*PuMU0{P}g|ZF{I_zm-(rK^B$|a{h81{SaRk
z|41sG#UsaQC!jWXmxur~TD^yoni}m34+ySb7YFR#ldnHJZ81y_3y!xN|21BMXU3q5
zloX4(LHx)*;18jtd6>xUcoiZ(#3~S3)!3PRyq*k1KDIiC*EKLO_%B8M;>C+x4_aRQ
zQ#!5=7?=s`iH&Q)EkHNL{r>IyP>Ua%-YY3_3SE9v|N5V-m#WN&AkPosaoxsm!xE4+
zhy2 at KU#JMr8FIdA7-|mC0!aC(w>J}_B)*!e>RwRV1mspDP7pLnXgKP at 0<cUwM~?!i
z4DPJyJJwUDPW_GXKHaz at gM-n}pYvn)TqFX(<G&0H_>k~r=wSa`G_f;h&TLib3Wn+m
zrmkdECM3|o#{=Gqnj$kZ*emQinB at 5vdtH9s@^W>RfZYRy87>;Q(bC-7s%B{TC3<tm
zws-~vPUQZ>F3Xc=@Z10x_3-d8`~&=?$Ruvvx at B<ej5eu@$JiUc%TC2FTNOszex2}A
zBYG1?JQ&Q{j_0sJXPamZD^;6wu1X|%H-Fi<Kg7w*?BX-)FDEbWKh)rtkdPoUW;Ye;
z+dlRKB);0IQ|G*$)YLGk_UfR#9Ct{oP4AEP8MbB{x4pv5tel)Qk!lhd$8wEDfH=Jj
zSLzzHDl*LIo=)$v>jJb(Jx`LTdHmW7`*)@b65&5)X5^-?mjj9RX;tAV^qz_8vMUl%
zGIOwZc0PUiG84 at L5fKp=H+N8YUTO&}U4$ABO(4C5N&lSZr%#{oVg%cu>0G+B*UZce
z4_)j(XV+kN;Rg>+PmP~fKYbe8CSwf!`t>W2|FAZjx~+|se20DsVWVP7KtO=quEK?4
z$C81r{{F})CeiW1GB-RH{^y?q09!mfJV<%4R)C4g3SipZ)RQVWI2dz16xT=ha at cvv
zupvbH`pV9~TQfzm2qA+ZxiSXqzy)Puaj~g%_0b0{33u+?`t(T`)XmZ+Q0`UdM_NMg
zN_boF at Dcm_f(_3pt6#&zrhw$zhN7aP9G^WOJ73*=6gNOjSd)E3QpG6-uWkA`SXh!)
z#VZ!JAA0P&8Y<U6c(Vd<OOd4YTi}rD5a8*4a*wy04jf73=kQ?DS`c3Fk(?Jb7Ilk?
zHvR344Q}(lvq0=R^yE|&7cHs0!r;uunV+_{wkQ7+#;;!s3NVmk&(Vh42Lwb!8p}Oq
z;!{%2x-o~)j$0HcK2=}|RSEj?#qd*4&r(ii=3W9?X6Ek~V~!m;LU!>L!}aUe<29er
zl80VWywKU(8x9Z$Ms8{_P*!UT0{doG)Rg}BM~*BkEUPW|k;-yd<?Hi@<ao%kGmwY2
z<mhF2_^=x>2;BX^MIZU5e~+J32)mq|+~JUXBSS+mMMY{*3c`kkq<=G{)NE|{L-U>Y
z-u(~)I&r)+Lls*Tfr+6ef!Kxph%wjk&Y`n54JtHQJ-3<6{OLq17YXl+nA{HBUP*%|
zmA92QSoHSxe)wKdQu5x8 at xRs*q at gO<B8H!TAL!1A+qYHR-6cn!Z>pSxSJ>X(CNnZK
z8wuITy{Gx??876%!jyzT1Yk#=WQ7Hrp!e2?*Q&zTMglp(k-V2b9A27p4)psXx|}kD
zl+k<>xWvn>tU3sC7cX7fZBq@}3S{^0?W7J_(nXA7spY9m56^v0=y+!Zk*W2mGOeCV
zxCad#d1ypz?61xjeAaeUR8;3k^|Tp_5x-sLw%G9ILgC%JcXTW)CoHU at zV#iYxO5u4
z_e)PQtx9vNq?lNBE~jakYr`S7AA_mVVqz{Ibp!?km}QIHW!d4&*VorOK{HXaA-I<{
zCQPZxKtqF+kDe?<W$dYiz23>lP}%03XBS_s7x}K4Cw;Ic#F9~NMa6*H4(6HIqlubV
z8~k~bSm6O=ycfB!B_ErtJE`wGY~7AW=+Dok0Bh4CmUOD^?4-rvAYg2|moH<6rj7su
z!JBrAr!QXc9z9C+>eVZ}r&i-pR#73Ww_LiH{T|P5fZ%<yJI+A9n_3QNi+6NoWh_=!
zR*OG>VrwPuX8(=luV5HYe=4Wl{a{_2;#?KUF6WtET>SZK>9w!_CgL~>&^7l(&CWMy
zJ&$m^S at G;y=!lfGbY^ZY4FN41TQr~y*`E1(_ZT3K_^j`zb-h6y9vohjD(6~o1EQIT
z+mgc#fr*6$n%%p6!oT<W`TDIC0~DbuKu-X$fR_1vzjP<5*;>NF)|M1ZLcVft>*3JV
z&7t=;V$5Qal9LxhstsIR^3L8I at QM9$VzBL3MPN*5_4LDV^a<%YYl$Tluc`Lby8eQz
zT|GU-qd$y0YeOj#9zSlMx~1P`n6K|YFy+EdO(Mxk1?vH1Nmf=CV-WH_DXC?*(U1@#
z&`wM7KIhL<w_Skkmppb@^~+sxW=dNpCv_vEl(Pio at 7}S)W^MUE4B4ucGV1$Q85$N=
z+uPfF<*hV?Y4|b0=takN2PrFecLu!3XJeZ at XcW>rFhCKbzPF&ibwudzSspgay>s_2
z;9c$Bukih))fcCbCX}^X=Hq?uvy10SJr?}uqW|%EEDZeX-^WyA_Fn3lbeKL9O2OpK
zTv1iU0V0Forq9Z^C9z-Q4N*Ae1$N;zpZ`G_HfrNaZ at T|+F`xzy+ia|DY%Kr${)rcT
z<mBWwLwtI`0#JPy78Q+cOr<q>z27tmc<ibG25485yko5W%Wb5$S309b(B*h at E8##;
zP!Nt=(AQ at sBqYQFdcahoef!GY4Wn=0cJ-lITM;6-tGC3#%uL|&bD<Yx;mT1)OI8k!
z#^z?yR-0oqR&perN${WQ>+4HHx`v`oY4FjnuC8*)$sN>I+~70y`I=~NN;*G1U9%9l
z<A=$0VkvFjSUcW&tb^<L at t?<bW+SJlXlOD){2n-P;J-Tslwb)7HFB6H%`GAEv0L?!
zwIK^EeTJjd`v%5`W#JV+ec!d%|JmB|F69#ui4E5r4Cc>{^3u_X4_7?I?Q-Ic`9O7b
z_0vrt5#FP|E-tj56*p=(H#Rr5N&f*nFgfNhR~1M2IfPbXlZA5M1D%&E$Ip2xbdQYe
zlylF5&ZP;w-S2E`8{t&}4?orfYW2&NG}GKEZZUJqP8B|SZWMQ%6`b_+<2Bkc(zcSU
zg at uJ$-)H;3eEB!H58~05p+ZGKN5E%m36{3GJED`IuCOWoui5@!oF3BDL_$nVjHk0W
zK%%u(OPl7p9?$?h%{MeO?ELhpzAjxb at pgE$|BV|PJ8iSZM+F6MJA*w&L_`p{6!8kC
z94j=;sU-Lg7#bEH4$(~vur10p-|Y<Jzg9qkuu_8K<dr?kupb6qR95AAhB+7-wpU*$
zjX{{m>vzE3I9;|OpcP%+-2Qh`4+x>lRkIhlbH__J?@f5{tv*eV8;iwmH|4mw$HLyk
z&`AvG=X at qxnaggLl<S3Hu9MXx#!M-a3+(ml`}gDH<K8b*Om?iNm}kA1xl0IVKDH4K
zX0N~W>r0vQqVX^A_|$7S%mHghKkoUM*NfVtz+<SL+qt;Q=Q!S-luWzJ4(k8{lASwu
z4tzDtFy~wE@}EC{HpB~7*npZa)YjIHE-ZA!9Ra&tH*N!K6fJL`FCdg at PtnUdrL9d9
z_~!3%874G5ejE!9#&i7m0SKA8(TO0Y3CORF5P<i}85B5u2DPe9E8`5l$QVTofoJQd
zurPsfDw}x^85kHXD=Rrf+z07if6L=(_wV1kdmK_L=hJPy%dDMBK-eRf)fL1 at pJVn)
zSLK!NvK9ALhoE%7mtB86-{@ZZY%2*NPu0YP-Ok?r!sW~1#l=#6bDnDI>d2j%o0~^1
zW}~Ai$#n;9YzkO_xgp8R^I9;4SA}4lZT6l6<9Ha=L7O?AE8%rPa|0z4WmAt;0|FGc
zx2BYMPn_7eAD9Nw`b@>e0`rgGzYFA6Do9C%jg47=B*3f+H~y5iMhYe|z%l<Zq5W)Z
z@)-|<-5O?Jz7$YgV&ULGW)Tt|&I^d9tgI|AGe5i`CoB8=d2IkT_^+c;ijFxbjpyYR
z^G`~FzV9Xw{p`7nYWw5Ii~b9KBXz*Soz^$$I*zf%$miVv&G_l_=h~JQ3T$y+U_*sB
z;QIO+6ntc5WD^q;B10<rH&Z+)ER>z}2%3G`sR&e5Rei3~PSH{rIW#gpBnz!IFd&T>
zWQ73ID<4~Cy6*dy=QuYv_x at PXM{y_KFcm)^;e;KynKc#H*4Bo1h9YKFvmc-Jo`_Iz
z9+o%lObS)Kd-pE)gO*pBpB%-cr1k-97a3B$GwlfeEKE99*|EV~Gca&aC4-hQ=q3rb
zez at G!Vv)YEy1RQJiPG5M(=J=%+w3wsmH8*H*xG&%8K1_argP^)fv7AV<|>G+8>=pJ
zn_9#c;uCR7jnB?L!0aEnh06VS6a~8_K>1 at 2eA4d#BSX>p;+Lj~i?Snb=jMu<)z8TH
z&an;KtQjPT6z54xNYKOP#cS~4p|lo%uol0ngF|+cqFkA8+bX8;rfsht2Fi&wX%JvK
zZ6$3T9Yf)Tu+9CGCp=h)nVx<#;JkvZzdsv0JNw;zr3MyDfX;Zg0F)w%@u-TSA*;l(
zV+r@}sWtr5oNXbMvQZXw{>4P-FN64s7cat at 5?w`ZvokOdfygT at Ej<Gf{|`?>MEi*k
z_rzIaMD{o_4tp+)&J8vU{IPGJDM{Vf*eDAr?*C>i^V8Rif|BylxDwUw4GnQt4>>F|
z at Z|Y<CsXDR3g!y(@~tV&1TJMAti3WLG!zsmw5vAuZ@#*o7 at wHnPS6;gnwrY*I8Vww
z8I=+Li(zSbxi*$vZgDi2#@AN>v*D8J`Wj>sHHkx2YMAbWQ&hzrRowe#ANLgr7Bhxg
z+2()0y8b1tssaq{NsC2t;3L0&d2-ZaCcwtZR7RIA0fB278z^a!fl8khdHEk at s+nEp
z3&ZdFN}M^V*=30e`lAO2jj^LSo@?ppF)j?t8(k|h#C|0xBRT at N-L^(al+vm9l=Ok7
z!2Xa=pFTC;+5VGepx@$DVzo9uwEi<UFsA#3HV%9n+1Xyh4v&Tel6$ErDMxV#ljkh+
zzb!3YeSHx(ZxY~1 at XY6&%s1*GgZudJlF6o0gWv{ABEnAAPk8KeYUjJf(|F}vJvST~
z?=f$)`MtKcwP#=;3PjWD&!0=1fL==TGczf)rJknI&02b`4L3%tJCd at d)DfA6tE&l;
zlao8)-M)RZ!NYKHlkY!%+^QRMA+s!?{e>ei%xehAEk{)JvJM#LFp`H7!b8BH at Ep5+
zroxzVW at l#HeAwlR-9^aj3905!1%F at ox4=Q_ATqAb&g#H#@D?r`%U1&0t8ewS9~8RD
z#TccqMb_+KQ_%0SwSFD at me)U4 at JrXr!igw3MkSUwCrDWM>5i%m_E7-@!dAxXjnY+P
z(_`@iPhY<#$MnYSrMN)s<%Yfl<ThPL0ACL19nNS0AzRt)sjR#QqJg}E`>m{|;hdZt
z<aSn8yf{e&__4dEXRT97-N*<hj&;)3Zh}|Ia~V at XmVM|@=r`Zy?5{2|>y_o2&&J*6
z;3MPb&A6D=BhqlmfLoAJz+y_xfjv at J@ara7lE|aI!_wvXmh$_8gP6vzGcq%Kw~4LA
z5LHdDQDEHKlwa)x{swH at +nWzoUp`3X%cT&NFKE4GsrrX@=I7_h*z^7oJOFGJXuL2Z
zMc_FtEz0*g{0SQC4H8o)J#}^Oktp at 7S{C$oe*Adr#}7N;;y~RQnM91MK{5FI`^P6I
z-v*Rf7W4G<l=67Lbd!QfWY>lZuF|~D%0k(P-NxTk%&T0xHvPy@*FaA%>@<fDQB6$^
zwqQU7tW9(6v)#38H6U!c7|1cB3<%s{=L}FUY>G9J8K-zli+?v!2XhI5&XoAnPkMIQ
zN+N?8S at l(@w-iU>@^hxw3K%FYDbZsNer~r$2s&o^jj{*d#tsViwJ|;_^ZL$6N+fSv
zSI&0+bE**+ZRf3x0b6(Xdn71#-*?nP at by#?7_ccQt2_-fbf at BWWF$80rXtR}>L<<#
zSaRufZtn^~v75%d0~-_+6cediI8g-yIzU=vWTg9}C1Mc!!$;rz&8Gx`vXy`FRfu|K
z(D at g^D_>oMP7>tnQ at +wA2Za))+X(o;N3SfHsKQ07)0BM;u at XxeRaN(j`d8oPf;frs
z0A6_OYFpXfce;W<R~J99!G`RXd7&Y{-~K83-CcHaauSCp)Byax937$5H86-gAf%TL
zY7CgmV-D)FwtXZd*dZP>3$eA?^2*B6pM^k908Kne!P_RQrdYsFgBRF3f!tC1SY~FX
zNR6W>B}UYxrR#WRedGvPQc{x0P)Ya4kF*R7lhb4SC9bHet9zeDFrII2yKVII*RSPX
z%;x96iyutn!Ag+;M2L9JXIqb=;*)NA{MeQsD3bLdAt4F0Y2Ng;a26!cMwC`!_{a|u
zCFcK_=?0H}FXOX|k$~C)4CDdtv$80_Bf{zMriMO}mXlNI8VT}a&~@c5{_ND at d%@*i
zzIrv5F}pI=QD-a3BPK?R4Zp!S-xd at kBqoLexg{bY@$>C-``FjVj2GHC0cE7>2Evf8
zAl`P)m(PeWFfnzMohpD=6n(!X5wM!cIfg@#W^&)3&low<+0_*^K5iKj5;D3tRwrHb
z5_3avdf4tfGE-Ai9JsK!+O4fB@?fNq?(RU_m`nPbH`FB5Y$pv2Sg_4Ltf%$Wt5y?@
zF#<=Ao-#G%@F+QKoy^R_k|bF_H=Jse)l=&rwbGmahP!>J><~Q}u-NsZ1K-yVf at F~w
ze%3v`{%!ClUUud7W|owAsdx`}bkOAI=lky`B?1T7x(=SavuPf;myl3?^%@y>Nb7Mx
zR6Nhk%BpLKV#0D;JTanf6&Dw;<Tdm4m8G%jE*ojPF2mOO_ePsk%zzf2qnyDx^7ifV
zCR>N7-Sow;C6 at UQW7&6+^7CiCVY-yid?O<x;QV-P4|0*4mp6DPJOAVX_PhJF^ZOl8
zMk#JBs^Wos=gd#63A(X16uvoMfoHIfIe0b*O?{^iZT%{FA8MHMYgMP2LC)g`15TM4
zs_?>A=sVjBJF&U`UxiR306_?H{CsTyM9bL+BBw4~p!<);^)kIU64ij`5FDd6q4zPP
zw8(H{sS$O0LP8KWz~klJ3+}*>%<J|AzyXB1uHXT)?pmp0GjQuE5-?hx1hCsBkwM{4
zZON1o4VHp0&)8IU99)!QrUbS9XjK73 at nNE}+{+I?`zY<wZ5r}x`&F6;)w$=6sBk+X
z-!N_gaHKIZ6N&)F+n6TG7`1Wg_4>NEsfRxQT_jAffaSA;PlYbOSrqfcbG|gCxSASe
z%=|?54GV-fLU-?C5(NCv$G6!n`Ga-si?o2-CKa9*fUjb#F*w<P#zxen+`ZlOB|8gC
z4fdLWU*kBGMz5+BcEHv17X%Y=mQUroch_h8@>Av?I*2hZzEJu at _I`6{A-%^A>45*v
z=E&*u=i41w#gjBacVM&PpuHq`jR4YJnTr<hyuF5f4&(f%hzL@=^LE`E78b at o*Pe6n
z)%wwye{D+!HfDHud4V}Wur;|gn0<+Nrj>bDk9w^D%9)hkDTtS&(9VyqFwAM}?2K=*
z2NKOqAL*T*9GY{AC!uAcuTLKs)9=7Lv?|So;*yf}s(lXjwzfALER|hc#E>U~`yM)b
zpG!f39mr$(v|D|dt&2;%=y0#C<W{L>hR(?s-OBJMxQ<=Xa&Jgj*mCHmL=q)W0;xn&
z>oeO}UBPEA^OTb=GM&T2?NJ=aKX3-Fk<r0yJt0uG;viD3t*uMlGqum2&CF9slEUFR
z*#8P!o*U+<J2{Ea*mR^wcXZeBmS~(gLx!UQ02lmx1?p9CrVaM#3fvqRLDtQ=y%@er
zA(l5ILW_k21zkXY;CtdQqkKzZ>=*>Gky~2&!JDP?Zs47cj*hkt4r*?0$9~Mrbai&#
zynmlDIxY_QLK;cQv17+J`0suIYN7I+s-T}TRAo<*VGJVUuGDk)@=|?v5tI5~bT3V9
zZf^35h)^#rEmbNj`}@nA at z%AIyb_Q|%3-4>S!kZ~m+SvLGNNSRR&}1(*rX*OXYtq7
zxfd!(6PkZs&`6<G`6fkvO`8r)o0Qw_ZjW8)?(N(MAXLhs&CMDd#Z`K=DU1(qi2J_d
zz`1Yz1y?Wp^G_&n1WHQEYT!f2?_s_En~MzlRy{H@(yhv0%BqNU7YGR0Iuq38sZ*y)
z*M|L&zG^(Xs7=!~0Zh(iZOAJVVkoAp=;fNuO>?)Mu`9w$9#SeQyo#q|Ve#o+01wBh
zkXyfd->ZX|y?y)k;%q_3lfuG#jLfYNlaKBWC0!VayM3FSpc*e_E?&F^>JUVTk9SB&
zyy)%F2L`1P9^UT5`lqJ>l(%IWB`x>xX6Q6?%7vGKjXKY)133VaA;}tZwlkUdZNK6>
zQ<|wG-{RwG0Uz*7ji<h$L0NzS6H_honL)kLM-r)I#J4+Q(F|#DaPZ6QY_)UesPIbJ
z$S4YQfD9WoDG?!77t6^V{O>kRQ<FT at p1*5kWZ~`N at C~{um5iHRwv!z+<mAbnUz3kk
zU5x|fcGWoOpu`D~24P|QK+IxEgolU6Zm-g?FdUIhOu at L=-X|QLdgqRcTZy$Hut#hL
zIecTy<Wp}i9_G?0u5)n&tXq)Vbf~~9M#RQ8BEXItjr_j<B3xBoE3*rS*VSUbzK0K)
zak$Pptgqtqf>4Zl at Y}Z%I9 at b6n^N-`J>V)%Y@~5~@%*{(=%x6MI?Md@{CqmBcgoFe
z0>}k3CU|y{j35F+$WSpDalZGGrLbY)_u`HPI*CgVkjg?z5_bt`d3pKz##8mov5vKs
zl?i31GGpp8<JkIoHLT+md0;hM>7!d3)oK9i`F&yGw3!(vjS~$er55XV&*6nZtq?LA
zn$zy?l0<}iEG;ehhv09~)`R=^t19_{S$|{rzhT<ila-zQ)YYqp;e9)YhGN>=&j$?j
z^bi*rwq at PfM@)R~yYC!FJv9~8&&9*ZCV&u#(yw0&?zZq~f28c^Cs$cni5<JYwmewT
z`E}>e+3(}klE)+^L$K_jjz*~-P}Cd@)TWIUQ<O0EAw(tPMYxP3^60`s1~8KfuLX5~
z|H>EPk}r-hkmKDx7*RMi`)`#f2M}gU{|e=zpu-8Q$2#PL|D(<&F7@@@K<KV7jw;U%
zl+dIF{5}FEdgtz491S;yMDX?jA%RKdW?`F>fs7tIpZFAym``>^TY9f_&wpoSVPk9E
zt2xhJ-Vcz%@19m{m;(xazsiMniXnH|)Rwj7wKXrY%Ta1P0ZQ;ZAi5Va(;2u!;*(l!
z3Nl{3B732o)`%_-G!v8@!AVzFiTlC^wRL at 9%es>DJKZxsbzbRiJ*@o`;N>N)e){yr
z3iy)G)UnURM->%0;Dhhp>JweKaDih>ZE|-jz<)t9V95}6C<NvT#z{&;gO^3#(!X(R
zw6!&tm{@0b9sgu`tIaW-o{T+AaiR|FJBRn^9xRn<ZfRLrU2JyL1vsj#lm~gnqo6R<
zUB}&x^HR==z6(*>+33Jl3z+SV^(ehJ&H?7qIC3iZGJtWxuTEdTE{^yNBrl?2{K#*j
zNj(;3=FuQW#^EaWX?oFdMdxAp2cw?q>iaHUy7cPO5pL;f%Y3beBe1+cB*$kw_wC!a
zzH*9;tTOU{xd1#}A&Z`tmOS6uQb at qA^YX-K0$5d^r-2Wh**kdS#*OcDbEnMA(pnmS
zm7S))6&&niE+-}R`B3#-K$^nyA~#IC5&VCQ*hYexQvT*pSJ&7_AYx1paiTLH-=b#a
z?2}kyk<%(Fgv6RJPQo4t3JGBiFsw?(^XL1ltgO)QaT4<1q^>bcw4~MJ?hdXouLg0P
zXWuJ=$x6V08yXt=iX9JPBOZVQZJO&}PPmK`VBbkRp`B_^Wr$AZ|MKMv*6HKK)oyL2
zKeef;tOpO~-`MoTafBHe8SB9AVh*SI-w7ybRq?D~$0j(#@BH%p)|_C6R0J5t;RPFq
zyhdVR&3`bcH?uwOvCHV5_O=~3(Tdd=x`G*9w&wZz>-G?-=@=M at L`6mQbMzjz*;HvX
z{yj6ptbAR;P%VB{2zfNd1&hbogXvUAlXCEyj~P3T$oTyd4hs*bqoWJj+VaP%SCBto
zFewEY7J4Vcj(T3s>hY(OH==cxO4(Qe{R4z1K0clxQmr;lv at d_(f!B|WKqn(DNKR(`
z at 2U)9cK!OgOo$5if58r!Ccrw7+r{PO<<&g0GK+KP&UsJ7NH3PW|3d{V6l<lh8VZw^
z0s;aab8sXPzW?2mi+Wczv#WE*%=#9D_N~QmrFyK^#f9GdHP_tUzTMi}|9>u%g at mwb
z6tGD`Lc-g&;^oVT*jF|J;dMdn?d at bHC4aAIW2>uAqU*@X4Lm!^aA4uk<Tom6>g6H@
zc6N4=b4;>wa$UW>O=$p?*gOiXhYKh-Jp4nYKt*i&A0L0R$une-AiPeTyAsnsf1lYq
zjK`%5`$66#Z6hLt7zm_0&Y)x-zK3rNcTRzEeo2p)f(Yoy at bNJZiaT1B=ZAx9ZpOw^
z7nYQ)%=gX~gQCMisWRYbn1UXW8XsRR`VpR?^XUS^v6MC$ulYx3ukyyIV|IbC!P}7V
z{FUY9_&ayTr^oKhFmrG;p60v}k}Bu*q_UC?gcpv}!XyBP{<l}!MQ&Bm?e_Tg9w+mk
zvp1{b+*v>Uqfo95b4&8_XrDcMh9gy-Kkz7|o)5>-mx1eboH!@4Q<Y{6=P7`6aIT5?
z;Qs03$6GjWI5SQWi+C{~91m5oH?*dvrY$!Iw_CJc)+q=Hm}Mex+oRq_*->g}2IBF{
zmjajy&BaA9JUpD&?;w_>1wukCFi<JbjFXcS)(X at s@&}Ozym)u?s!vgHpDHZgbHGqM
z=EK3;TkF>gi;HmxI|Y-7T0npz)^$%#P4ON-egIzfF~_{)SD>}s-4h=-41ogY=vA<p
z$uI>*#>Au+t`zNNxr7>vyjW~zdvWm<W+<Aj-#pM2efu_Wv^OpM4_Xuyj8zmL<mZcH
z6KFJ5oeLL&CMT`^Ck~EO!^hSnJ}{LI#0kXnL@<rwBB(UU|5b8gBO9DjxYe<<)p4$E
zCRvMLS66qj^A|V*CX~y~m+n1u=n&p7y?y)krA{^F)qH(C1cVQR$4OHiX-%ONOk$Ff
zM6e=HpFUlzmZET>AR^SJNr`81w6-QnO--$|*h}GDaKig at Bq~4GvPa=1L#l&?kAvbx
zZCqSvecuf~6Y_F)7A-V9O5-{6)Au`kxyxdjapf>D&qIZ6DuaYU1n>pSY+?U@*Y<}G
zh0sb2{6CEMF{oMK1Z|>E+}+(5b&6L`9dLGb*5IS(DEFMd*;bsM&P5ZLT$WQ(!i13!
z$I>rPo~f$V7ED|wF2u>kKz;w1v=IQS$9yF`J6>wytaB={>VAF>hv#B{nZ-pHL{q<?
zJ(sbfZ~Xnv=KH1YwCzt2ta>r=``*2KyW at +aM60}8hI_3g#ufs1<WLxc at WxyUoO~?h
z(07s|-$wk|#YoJ^+|4cpds+Ye8wVHvhuoSwUi1<a`ofG4PMKeRV?4GtFfd>t$-2kb
z#HCjD(!XC_;j`gkHE7>+!i;9p#y0u-#<vWBag&Ey)+$c5KO7qyi~nF9ufZo at -PXvR
zsOhsd*Uw?c3(M!m9HWlOPvC)HzkV(CUg9AnB*aO^*{1K3nbs*NC{|W=oXfzrKykZ!
zcw|&mq%b|RjypgedX at m-w8Fj0=KV-&-0|Id!^md$V}Mye0f18_9lHwKK&7OlBy3WC
zkEzSJ%Krq`Q%#T2FNYpU)ZE%$d*76{vvn070S9vK4!ZQ5Gfa+4$#jr92t05RXKsEt
zoVL2^(kwaUBD46}$a#!JX?@SFTelX!*ymcb+&`K&^UhtC9os4axy7Id%ocb&X2m@}
zmy#Fo$CsS5jUFQ5+}zyDoE#)7BbbnOQZTXHeE^_V{o+OXKc+mBj6f0|ik<HGHg|xW
z7~Y?NOHlAONio=W9MEzua8?RN;dD##kimU61xX|&MPREQa!NL1PO0Zff@?;XeFFmn
zK?MKR6#%Q4UHM85U?EUJLgJ%p8fs%LHH`d80|HMKP6<3chdJ?ztTACExkIXbf{Fir
zt!=LKS&6 at QPjXC(n|lw4O^$88dIJ!-f6KPp+S+j3H6R<dt4b8QjE#LGBO at Pk2$m_s
zXGAdv=89dcSAYJ at 0GOmvSfwp?98jlFcdBg>eIL!aHA><=SJb<h<9wq9Of}5{hbQ1Q
zI?e;f$`_|;zkxYVly~np+MKb88 at Ttl+{gxX)pBBP=lF)fo7`L)zqR?@eCh9<+}I8d
z6YEC;mGJcKhv(U5$<MvL($@$o-o0Zd4<!V~P~CGrv3*G9QH7hvvyVq8RKUlv!i9Cz
zW=LxD_wV(UKU-T|^pW1uPK=AUqoP!S`rutoqUPAtl=PvA!+4bogaJ#;aXgfe5ax1N
zlnh|R{x7-g>%WNw<NnuNT;jccTh`c5i5077?x~<;V_>L{y7;#C9&lDzsmkw~Cr{oC
zSE5UrZ)|G1aOqO8Ra0!nlP83jNxHZwR&Oc3yt0yS*+34w>H^(4iJ4D5J!8G^w~u?y
z50V9)y!qm>FL|88FJ_N6e$3kksKC}}yVvgFt$<`4Bb&o(i@=A*u|U;1U?nBx$7_Y8
z- at kovfGbX71i%?}k;(Jla2>uPcldBP`HwaU_<x`TMaJg-rXq)ghg*HPb4b206+Ry<
zKc<lYbGFvYH_QQwnk?2a@;7M}b4duW2^pAZDN#v3BOTq?n6`5BAdoHNayR=TL%`9O
zJ$CSCK$^!7o8D;xE{JCTzse0SB_`Z6zFWV?#Xt`0{eDrL9i>7X9^y)rT+r~lt%Cqd
zy0Hb^32$lOjf;z=I2y4MlB~K%^8v*EyJ2}#Z7JapE@=YkU5^6409W%MyJ9cNN<>V|
zEiKK2iN5)k#CmUx&-|l#HU0j5M&y<Y at 76dl{peH_<5)&G2M$CeDl?v+9&3}?ozv$x
ze*!Z-{&$&kaMf(*ry15vV}JtzH?XXLtB+;X*wPV0JFYn9iE%SVIk&9wc83Eco*$~f
zg9ebkSg?T1VKj(D=i)^PAwq$WR!I;yc-3?EECtZs>l at 2gs`Tw4DpmK09g_>ZPHkSW
ziRDS#4ASimQmU_X%R|ZxZW{ePugUCMbV`LFj)meaUn^I<z;G7^k349Bw|_N511ov(
zV{dOb&IiA%tyohNj0H{6g at uwF|C$!qv_+?AyUMVEdJ{>_H6W-cDA+$VG_>`Qu+(s{
z;l#~3w&wdSUm5uL(xU3dP6BXIv&&A>{|i4u-Tpb-Ah@<x<;R_KHiv*T?j at l%2kSys
zsi(*I*ykII{9^T9-Ngqk10~K)5QIom&!3*koqRsmUqAw}MuvxnC(o*h8hG^9{mO;W
zQ7)WKGg`d|%@i;cVl9UKN}ppM{f`^8`1k*qnK7vy`1$iEkRVcOYCM()%Di#^&K;Gp
z8!Arw!|V1y6a)J$EGo(<DmsvplY^J&z^N>X4EKP^bsj2BO-{ZId(D01NF=!s?Krno
zN=$f>pRccXdd1XEzSoGw1!KpZ>i4U!>si%kp9QD$a9I-NTP)-=MJq(+Z+$%HZ|Se~
zr0a4FtJSBM`BD6yIu~Zsxt=_UxFwKING>HyG5B7|Kk32S>iKf93YWk)Q509MUY$=m
z;r6XHsch=$(@_8ojdSNt+1Uw#N(uvL&vYjbOGuy-78W+Qvm<wNb9<PaOfD+!)c^KS
zYip~yjZNd)Jd0H}^#q?|BXib>%$bV&E_bA?qQ&)!K1TLkzkXds#S`=j{r>&C-LAxr
zAJ35_8yFmX<jnUv5_U&TO>GoZ29hj1VgwzGV`PErs$49tsE|`yeEJ-p<elG|C1|mT
zh=`j>Ni2=A>``R<`O{UysV&1_6ysb5STqYzNDt+G-u8`&ouH+|vEe{L04OrAwA?>l
zoDDXGi5&-}9ICS}=i+=}%#(ie=FRb9t>>~>*4v at 9gLfr(+!P%hEoR+Ji&M<ut-mGM
zvs`)$tHCQP%bK5`9}yGN0EGGa!mtA0;lq0gAe2}*JJZO?Rgj<QV`OCf`9oQVN9Avo
z#Nzsrm3+V!GeB$Y`@0}Gl9L6ab<(r4qV69xx_ at R=3Vi=gM{8T#MusJ<VAGwucX2*A
z<}!k?1k#K3B{;TR at y72@-;Y0#z*CeHCk!;IMb0#eYy`TxN&p`3Aut3rbnI9#_^ueC
z=k&<^`}f5pB<Ou&^i41+=h)SD(nyn6uU^5j=H`l)d(7+;5)#U<v(9d$rK9FljOGkj
z-_zCAwYaiU$7JRoWctoGlAZOEvT7J*5J1uPpEa|5RSP>o2=dru4g1ys5ovuWt~%W!
zz;XqXOfjA6_U+r=_vU<`LR=LW7l+-dF0$`+nfr2lu-t=!n3&kMFOPe0aB%e7w|!n-
zUf$l`E^=}ChL}oTUtcc~IdJI1Agq&shzK^enVp>-{r;Ww^y$+wCr;GV)a;?AriS&{
z?{|RNY6s^^ccdwuHZ){?Ebns*Hg_zvQTwrcP4^4!P_>08Pk6 at sVpxygN=izKuXc2D
ziiPNFrZFpWX7!Es8ZB7to6-NrVm)|%WuA@;p+0lz(xtvHUuqj0_en`f<pJnyJ32TB
zFB^)TVqj$Ck&w8k at h={f0Nn=%;Flkqnwz5{AtB*#JQ;I_j~=&fpxCkQ!`;Ic4i3>4
zbzv5 at A!In+TWNABc6)t-1_xyJm3s(n{rS^C9e!lAEkzpJftg!dhk$7Kw0>3qlr^4F
zV6S6f@?o%L-ynP_2k%>2($~ggi0Yudm`Q;?gL8(r)|VcNSrE#4&I)*XN547{sdlTv
zYhnCzj!|udB<!oknKOF`B(HrWI)DCrUP(zf$kN3H_^n&25Vl3Nn4DWC8~octNL^h$
zOzd4ohD9jR{GEi-vv(?}sHjdE^ENVD{d at 1uom+o4*LWd9f^ph at DLjH5ivhGVuXa2Z
zJWd{?#-nU$$wN<1zqmN2D(}CZr^Vx`sd-!MV{BDbKxjg)QHj%0KFbn^{yS#H^>)Ck
z8L%Py{=?_Fz*v9({*8^D(-d!X=9($V$jC&n^H>#)_vKsRr1&>x?_;qy@<`y0!j~fZ
z!=CzDrw%YNWnSxkj=u&3<Mou$ilL*!sZhekrL at OvTC0<f+uLQ&JxELp1rOj75Fl-e
z<786rpYMJ_vyX-&y6VQ}6Ij&aZylb?VjJp}Z|AtLZNjTZL`EuGSa2sxUc1PDZD44K
z{oA*1mf@}X?-Ei{geqx&wWrDf2%OT=qWqgtlm6@(PW7n%@PRBgHrB<(<z{9k at 0Vi7
zmX3?!&uhUgLe%E(5Y2!3q>EU93CHKpGpf&uWITID4AzE?m;pCx<FK8_0?_i?rFzlg
zjZeq^FB{GOeJdjV;X{Ez8G$pku|NYYEuVZ+b1*U*kT}!tzjE@=fddD|e*c_Nxrpi%
zb6OdX8j at zd0l8G$-hO^oCy9RWogWi&f8eG#at=_XdSrw%cpufRprD|>u^uvP%C|UH
zheHSaSK6d4!|R5zHf(TcNZe=H8ZENutrkx$h?hLO9zkF)HDA84;{5B$4|g{n8O6!_
zu9C`m&Q|}L8=&Llq+w#(9nCS(6c-M@*f%Er{30JcnbS~tD4;W<KRhmu)mGyPs;W|a
zl$zT0#zd~Pv^3qM!qc|*4Z9NQg(z27*Qfk0O0aTZqTZ|D>EH(;(+8!UjQN8qR&56n
z?2a9q=ptpd(hB`LH8s(kz*|c(rlqAN=Fle=&7r`m;JbPg{*`S$EO6qP+VdJ3wA(v?
z%o&`Y>g$OdzZBB7D}3Gm7HsQA*XN)fZM6`H5##V}gVN4ca7YMIUtgbjXS(wDff7-F
ze}CN{&q$(s3P(rH(a?bWFiYCfZf}n4xVlPZ>48;a|95ykoCX66_~RzviY0=;r&s$i
z`H7b|lhw>-`a-d&aVf#MbLZR_hS>pXYes8=yWU=DVT}uV753;6o7$;U+~60zZ%kCx
z)QILs-pAo|t>om|p&=8E!FhPGwNMGY7FbWPLLC0VA0+p;MOSBM=gq{#M3<4;h6duf
zfs*i|qT}z??u#G4sH4Nh=lo!BMN?B#aD`{$W8~nzefw;O%8x?`3T0w`uywDp`QfG)
zvsJd|bu_si=SNRX&dQug5pUEtxto?2S6V8AHncWBwBKppV1<{qHy4D55hnE-5d~JM
z!<M}Y4Gj&Y*C+P1KROW%a5f5J%zNb<)o|d>AFxvSADTmW*Lm|M0iF+H<IT5kYr%HV
zy7VCuu$!j3{+czwp@`tK_5UUcAx74KtKxtN6tJ*TuX?VGRCv*3>E&SN8b;IU?iW_S
zX8Ub>UjLKRSW|xhnC|<X?Soh~Hq)Eim33JZ&9E+<X6k``p6 at hZ7LqSaGNq#ng013G
zQsO+9EExo(10qys)eXNw`(Ene;^HU#!Q%au-b*TWgkIA-ObiSJ1VG?I!~YRJdbEYF
zD=}IF-d<>#71rmE+s^h4>|R;nIi~`V;rH4?T%6)>Qh?0dq6^P}J+<(eKG;c3O{F5}
z?CMg6$M1Z8?x4PbLCwGalA{%Fk32nd*D&%y{trx61;(>Jl@=KP=n>`Bt5+#G71?;z
zqenw@^+X$*nylsr%Sb6HaTfT}M4S at Z#BAQfWc at 1t69IpIUY7U!Majy_iq?md2-Y_?
zGCXEJW62=6g`H!+s9BYoxj7fceuQ5kAqpoaCuUi90l-B{CZ-35QK7HGq at 9P+KWl9V
z=;-J)G&BeZ`uh7bz}a!A%mWdV(b?HnQDti_BG_{DOQ(^lnD5_hG1`|n4XFTZ0Q8yL
z+D3rFegeL5<xUAKwXo;+XmMhYsbr!O5RcRW#9Ve}mL{4sw6ui5CK0p#Bz_HCKQ$jM
z{@nl3(>7+Sp9c?L>!m;c(FT6<#<a>m5;m5?+4AU_=79A at EF4eI$%#P(?&_ig1csok
z3Nd(WbW|rxI at VzBEWFL%wlR23ARoBR1~K<EJY&dHhr@>t0};|>M2p$}O#vDqSd9lE
zQ2ffj46EO}f=6nN_ksi*9UBV*6=B&F%f6L&nO(yEvoIEq2?~+}vJ_QqMMqOQ43^T(
ze9oyWxY{A+zwUyq#8TzF-u%>m=&h-KXt2nhiE!`UjNII~e5)qQ->cJGIucpkmR45R
zx3_$-<^XFj?jF|07CDzMU&aYg92^`jUS8obF)@7^Z&_8;a561`$>R4eUcN(z5;y)p
zoSppkjgUa{_;Cv`xoCgG0ESNx*;h1(V5!vbz~e^`rmB$w;nPZyqJr0dlhE}%Dk^FT
z0Pq!AA2ujIckaM*$KlGdcz2WSlF(4%_w}K#vLU8h0QMkLd6JnqHa>pqLZ-UKWOD*A
z2qroCMl}eAQDy-f#GtbgVBlRz%*@R4>n!b_2kvYm+50s=B;+ZO9qR*|<ojzjQay&1
zn~zWJ-sT);X8HXR#&ap`?f_&7aDZODZ~jY>(Ba9?msnU>N?pI+UUd8U;~#4qo7(1P
zQdo=cgJs8X2=4T>0dKEMw0J*W?m#du^_aOIU&+qSel)t;-o|DhtX^qZSxs9T)%^VY
zqwEj*>~|hN7U6Svu<CX{fbNQ^DF?wxGc!&-J-xi*;`_<<ZEIB!I|BY6Q~MzyA>lI7
zNQI4h`1$v(EsUJj(u!Ubdjgthc5V(>e;k;0?eH)MkW(<ai3<%saLRB<NC=mV4D$=^
zG at QXmNk!FFWPcf((PGPTKnIz~LTW*uhi)KQF!)79QP{LT47xtixMXzSUI&yTPLKpT
z7{w&&YGTv`J6sFQH|C(Ux`sxCt>}q(4L<DH`1+qLS{4>EQBhGewYo5>iSX<nNw{wS
z&nQg+Blf~tXr(E#Pc+6%FfE7`t&KNOU>jYWm3ia0<j0R6J6~Sl_4f121ah{xv=jtr
zcKC9>#ABsEQ~v96;^O&Q7uNtnL$5)g65?}u5dHE?xraS73rl1vKP=Jj2vYC}tnhRB
z^V<U>8TRf4emb&YINg(V`3imR*=;maBnF at X2(}k8>X$RkA8d{E_35)@{i~k?7FYvB
z(q(<of|;4w9LcQ26>@;#dg>VQ3)tNW`2|>6zHJwe|F8b5gCvcUdOA8c8)FVm1a40S
zin)E$%CEEV=mhrPm187bXxBrotgQTGn3IPGfONN1&vlX%thlT6x)mj}7(p-zjXG$(
zvK-?$<V^EJ6_|MekBd_u;JIk&va_>8LL6af3!O(eWMyS{=^uDHe{}H;_ at MZ*+kZB>
zcnWR1R5ZSboEdcLdJ@{leXQ|bbj0H7bT`Nu_*~wLqkDi3$HV{AvUhT#2AKfF7EuZe
z#>Lh37I47``)X#Z*D-esL6!te at XZ<4p7{doPd60UG%(C3e`h?TNIUa@{M1fUBql(O
z{FliysW%k^pMUo3(BYBK$1s6}h6YP8>Dbt!^YX+PfFJ|zejlp9z66UkB%F&W$7b?i
zImd8lY=&w$Hfie0JZ;q&&9WtS^(Yq)(j}0(m<bJ(pn*EF=O$n_4pIPg6#CYlD&#4H
z$KY7758<?~ww4g!uCef1_u{bM0;tBUE1(VVI)sprkbodlJ*F09^yt^Ghg_|rKZt-A
zVY>-HdpcHD%pgUJ2?z+lw#fwr1-7Ei<{ovh)d>$D>ht?lOzwanY;JC*eeU at 2>6y?A
zrDu1(xgE92R+PIwPK4R1&CME=?6M3y8y$hL%Oqvr5|Wc|14kJ<tf*+J at o&J!@*#qg
zmoDwMv9U?yw_FKs$`-*o)3LhneD~>#A>rY5y}eA`&(GBX;9EdI1o;#x2+<HN4gd{1
z^b4C+J(>x4o(IlY+tfs|w6p}E!6?V3cN)TQ=Gg}#I5*96wvXFW<`z&zHn3MD(ZIkn
zGBblY6nsfD9o#-r`vH<AB^1MgE&l##4~AY1y5jNU$KO9bqX2jJnKW(Gt%h9=37LZr
zWnAG&1 at FYg^Drri?99L+QR7i2xnp`ISFT>Ah#Y|5p#A`CnIC>9G`ME>=_TUP*yJQ1
zpOc)dZ0*O7cg!N%n6uV)ncENP^B><F+C?`lN>Itl?(T(){GTvX1C^tXBq}d2$Dt&~
z#>S%n^FZA*fqr8Oa|3lsq`;9QWQZ0XKNvvx<HG|_BjvraHrv0yE&2F<IW4_l5$S>0
z|K$SUm_ at g#^GJkBoJZ7wCx}@!Qbw~Lzs>nA`xJJ$zeP;p0&#eKeLaGmI!hVnuzq}Y
z_BzNf(A~G$sc)9h8027wo??eR1X_>fS=_$020;vLPRRfA3*H|);Gl>BKm-aBjofd%
zKGOEn%LEV at dNnIk9r?~9ikOy?kdTm<msfpr&t9CgjkXpR7G at dV{3D^YRSP2M7#PBp
zJ7s6pzX5v<0$Ypc)8B4_)jQ2%8QpURFN)>7L~uqH=tRt4{D=SQix)32{|tkHzklVT
zoT4I!^Kj)J0%lg$|IQ3lcsxI#yjIoL&Mnr9%QrlwhLarweor4Bd>_cE8cw}CeDZv%
zEclC+Ad-B5XEgi#D=mybeRXEzA0``ttcB<j2L2MsPM$S^^IyQ4%y9&RtjFuVF<n$}
z)zv4l{`~OyM<>sprv`B%<~pen%`AQ^P0pNPxJu*u at H;<J3W~Z!q04yu3tSLE0k|M&
z?Xb8wnsU!MazLg0x+~FRbAObtMGjzvfvc+u>|5uvvrOJTJ~gNvU0AG&CO1~5O5LWY
zk+LidD_~Rvy#b3vK!B|mWMoja;lK_&<oWS~+{(%-f%j^3{+BvLG`;uN?|2^F)wEW}
zU*+Y|dd&|iTU$SkkG=srKSmV>6wKxNb!CW;A)J1Gezx}ZQEVsn8~uNLy>(PpTh}*y
zVK-7LE!`jpD6MpNNUMO-D5W%tN=kQkhafFs&`6hrpmcZVH`h7$``q96ykmU)amG06
z#kKd^bN*tkxz|)u!a;S8#Kha^EaR at Ot~=b^;y`fk>&$BiwS>@7)9BUAQVZhs^>t(t
z&;U_VK6`w=V#gT}KmQE%a^ABBj?`a#@#t!ZB=ze0`rG(;GFIKnGgq%(Efm46a at aTr
zxz~W+?GFuo2lfDUWX<;H#r#TQ#{r*;#=N7q3en(;m*b-7E85%JpL#ajLf1yXr2c1j
zZQ>3mXWeX93bHa}M{!xjAXPZ1UPSoGDz0zL_c38m&m%K4Go#$NWp}D~liBxM2I~}*
z=!I-F3LO3hRn?G8z^npGQ>^X7v-1iF0FY<Ry1BKv`PjnZDjy#on9;BO{Wrjzd at 3w_
z19neZSA|`j*mAVW8x58MPZi1Uz)^F!F0<TWwCIL|g9e44y?o;<px1Dj)lAptC}qe!
z^gG;+wjzEdeRBL$P<#9x7U4PpL0UJPa$9Sw2<T-%aI{lv`26_~_$Y#MFC3~}nm at UI
z;0$i_^OK;{>fc|!iH4uTKT2`fn1-mjfm=3QV%A7y&5=$CDgreFz5DP1%>+SC*3yzS
zn#=4Q5G%#Y;oraG^p86p34}yNX at b}QEj>`6-w1qy4x4~G1rZkp%=+t7(yu3X8y<c9
zzlwH7(vp%7(7Yxva%5!wnVIzHVnHlg43~<5ly9Xr+K}|jEe1xL=*?0=-P&LRAdfF2
zNB`&Ro7sT^a%UHpBv3u}k-0Y at H>l|8FMxIALyhb}a(~ggX7Ev-K at n<L>KAr)?}2UQ
zWAA^%ju^m$b$ADs5mog-T+1!KV5Dbc{0`xRrVhegCmS^SqgE3TzV$P{j`Vl$;-T#Z
z2s4yG2#*q6wh6&={lu0j)VfJT<U{Uuh?5~7Ck$(ahQ)#FizUZAwfhbr$q4G}keZGT
zwRd0wzPH^{VFQ^QiRQ4!@xBKnOM$yaiUUpaA}0^n+_c4 at eiV>0j|~h?LrOidHHTFN
z_y$T;ndEvR>&(N$gT at aOx*j=}fBAymFo8QH;A3u36 at T~;D_t%I)!xU^fH82Q?K9Zv
z_=AIk#v|qIrRIZ_{I6Rd$sMfzzzzxyPPS%q%##IWjZWbNcTd7+kIuh9 at 5*N?r64=R
zX)&a_cs<G|NRqnTeu)UZHiXW at 0OWrSEKS61?u9M5`#^GJdv(<vc=6X?j)v9k@@m&3
z-tXVPqj#QAe=+DK^nyZ01|9Mf at d8dDj6+uF?y4)$(9uz$YDt0qP1lU{bYUQUfZ`W6
zHX>lF-rxMZ%Ef1p%ldyskeC2CWw5ux!uFz~XS#cOfV5A8-G1%p`2EL^)5w5XTaQoP
zQpdvi3wjgH7f0fSjO-c<3kzgXB3PYU_g~yLx3H+c7SfljQJ{ym-BDvY2*1u84L0ZX
zW;#?L8la$zflxq at 4$K<~<_XA849E)fGv5mchcH(uVTfg<tlW<G&I8W>Kwa at scb{k5
z{>*)L9S1$;60-qHdU`MY&2Oe^j*h&~I^sWq!b?IkkJlQ>LCRr+0n6=|q{an$Qv++0
z^5SkoU3gd+jh~mx$+1gaW8(x+6ROaFZN^|wZ(DIOzWDdjk`e+?lf}jF09cyLzD3Ua
z1Ozlf!lNyW!~KN<5am8v)aLB!+Sb<AcZ at r4BMl5ff3`0t;ul3BFEu32(ZP;{wDcue
zu&LKP`_O2iPbDSj-OM|tz4V1Yg3vI at w{PEmLR%aHj`-jLQDGbn{sJv(J$5D}yS)4s
z at WMA>n)h`}V4%K#{fdEyfSk#{hlX_~K0J-s>Zkz37NpoXYJIbrzJiN~_XM5TX=O!W
z5`)K1m@^j7 at 1P8*0>HzjSM}9trP?!J_e(O^93<F4>(pHfV{gF!{=d1Y2)Qee(TFi|
zaNeLwAS^2eqphutCOoTT$p16q(RTJLRb8PpsyCv8^boSVeY-l at j29Ul{S+a~a2XqT
z{L?^!g+B~S7C}{^*K+NbhR#}8SfFYY;M at lf4-a4%3{u<yWCEXn{&x=!P+%B=rg)Q*
zLWL&(fzI(El`lbiLzcgJQI!FVR3xVfE^4=gv at R4ephP{NXx2)ec9}3`&_5;$f{lcf
zwEo)1zK6h?C`d7n!6X9YB{bo%r#p at s;ai%SnUQ6Mv(w^vi at rUNP4)+zke9D7p6mF;
z#2VZS#yvfqmYId+iK(d%@MAKnwwIa{qWUJc#n1x at 35m&)$FfJL;sYYyxG$Rk9is~o
z>`jVmzz8XM{`C24bxXr#=$xElvjO?Vz(=GTeH!`{ckWyVb$EjhU%dmhDuTxl1#3N2
znqp&P^YU=_C2FvM#P9$U)DbVh4x$DP(ECWlh07<J{v9sw<y<!n#Y?2VAcPTU1y_Rl
zDJNl17bfF>?SA)p`=6gtchR8>kS5XG7DziKf;YZ(=P^dB)SSUS(vKY{tSWM9q{0U6
z`JoPIcnm*AMo1u`>H{f6{>;pr*VWZ66fyE+k9zUHs-cmb at 87@o^7E at hEpxcIZ_#N}
ze0;=hG50(HoOtc#si~iBv{9?aB_~tH3c3)Xh7fcT at pKz8D(sv-cLk|ZP)RLR*}W<t
zw!Rk`wW8jpU&F(wl8GK~t)@=B?cy&Sxft&HER~G9Ywx`cVNK9qS^lm}<F-22-qP|N
zvKe(@qw^c}YhBS(2WVulH<mr4si}$D;}M|1vqgn|-y1CBsaWZpCMG7RuJr!>KgXT|
zuKB|AGR??VDkq1v0}TCVSF_d6Tzh at Ez5N>8)lCPMTLvWzaEpb7h2>5=tp6D^2!ayz
zR#66_ldPtvr-kF57#L9X5~{1KH#Ik-Y69q*>$h$-x#0L!djxO40O5}Qm+h~iEPC4?
ziC0+mnKKsZR4A+<MbVJ4r=H*EqpGU-(bkC2(I#HHd>o_w{S{`2C{%6$>y3g0jqZN6
zGJ1>46kpu$*0&`R^}Ntfe4X+a4<$!xK&WC+1s*6!4CLb9rKOKMqfQEwl7t3dhedw^
z4uG)&ZV&;|g6H)P?bQAH^aO4A at yRwsHi6W`Atdxhx)ZnzC<4(QES2X)JX-L>DvM|z
zQTl(^jLuapNdmA6OG;iqht2 at QQ3C0+XcfB`6cnufAnlM<udlC1)dtjv3^<6Yk#W3s
z-{DE2D0f{j7Z4XkuP&pP%^|8#N8(px7v7vcZ(US#iTBkqN#&~*e=n~Kz}Ns4co%U{
zr7KUX<U4?9-8BhT_ySb#K+hd at A;KD<nQaaJq?bXa7yd9P$pw^k+FnF6wUA%#5IP~b
ze1q8?Z2={*bD`1-TpT*a0aS<wxT0Sa?CNxB85t9>?&WSLf*>&7jlF#N5}q&?1|5wp
z%#mJQEwH$_h=c}63{LZ3ij!QA{TLXjs7|x9vqQk-uiBsub6VQBUg)G?0G|Ks>w;)`
zk3PJ#^u*9mb<Wunn;hNTNcqdta?zYw^;}PLEkQE?&D at L^bn$_|lIMOR>jNKq(h|ym
zR%(A|h0Xqc?zNVtrU#(Y*8m5fHU(0k;&&3dx1^*but0cx_-QTe?F|6t at 84OFl9Hls
zP?Sq(J6ujqu296#^v_gl1ZqY=j}G8 at 96-CXw^!8j8fhmmE80gJ9*)r`X^8Y`YHj_2
z=AN$pd59eU+}zv?d;5mYBoX#!KW`OV46CE?V7$%4L-^&@N&}Gobz<VnV6>2(Jw86Z
z1B=VY4^qVaf7N}|-O^H1{~$&Izb&62_z`tsfUrhhBrtRd9KL;|Y#Z7h_|Mz)zzToC
z1hX47JYM`&h^nZ3{Irw+=cs97u*k^E+xr_J&Fa>GehR1ruq at ek9)HE4cWQw-A<snZ
zA(O5Uab5uF^4`<a^VeT`Y7kPudkr-%2c4->cu2wQ?oI at B6`QC4*OcTYCZgl{e*_2}
zd!g;;-<5X2b_KtluybwI-_(B$W){|a!qes!9;haeOGd$^E5E;Z9_*}4!0H3PV%*V@
zRomO!1mb^63k(0=EW49_%IgA}fc9!dFUoKcV8zhT5Q8D&G{*c-(M(CQskEr*E$9=S
zR&5Yi(?6p;(4_L+-QGGzc6BRsRu`%-0CWEtAE&gx at 0S$|a{EV54<4#f0l?OQDp5Lr
zjpS=2yFp-ld^=m~<76*?e^ir2r;Pxg75>P=DbTAHe((T;lp2^9R852 at wz{+9SyEE+
z>n}T3aC|)K;k+z`=BsyhcMF4HM^h-#E(^#5YyBCHhR}k{%nJzC!AtmhoX4hwNML1U
z{eD*wZS?SoHbh=RD){*Lz-n={nm8&d>K2FL1z64Zz*DP_Y5ds3zWpy{k|P8L_r>4u
zXHlOrL<njPsHqXGs;=&WFW;Q&IS*ej!c#YCzq721&WA^w1B&d4k<oj!<M1<*9b)tb
zgh$chqWK{N1e(Bv`X}IT$8!p5#*329J$?EV)l}e$*Jp{ZU{DDFlKDEFeC&0;&{yl5
zb1KNMBD$8BzeXz;{SNqVz13ts3AEZ%@XYsL{K25R089L}vr}>LQzTJ^-NKELk&#sZ
zu_xWBQjnInm?S8J{y-=wOZ<_Smp_N|5Y&2jcsPoNk`iRFii?Zu^sY9{<3k|t?!Nv{
zso}rW6NEO=(D1yWQVVTb!ePJ>06=BwD<Bp;^72 at wI}!rKd~Yxq#rySXtgWrRc=__%
z`cx~LU0nF%iR{$m)Kn69Gvn^mi|z3Ot<CDW4bF#ajpg7r^7X12?dfyLJ{J~#3uRD2
zXEqdyWW9v%Mg;)$z(84lYi+$8LZ{FGcK^xKr<C at uW!qBI(*FKOiz1<(no1oR8JP&r
zV0B|d1d<i;B8ZMYC$vH8yvMP<{M#3v&ICx^)y>U1g#RzcML8`;9wF$UZ0+c1L=&38
zcOj1iR{vYB=I1*u`?d&N7gvFGNa#$!6(6ND*kf`nJ3BiwdzwFtx$KaFO0K&mk`2!g
zb=6-b;(Xw7?&=@lKM~Js&0GP&M|4XP(ExGiz`>Ra35C2=G&E=7k=7+X^g#KJLJ7D4
zAuecIRAqvw{@cn{D++mHn%7QEdAQLbgyL1J*cb~W(k)`*ANBQT!Mn2O>r^N|+pknk
zh$v_+E-88P?AeDPDhbpzv$#5m!UtK2+0LZXv9YlzBG9Dda>p%3q}ga>!s%HqmSu0e
z$(`|frCVCm7sjC*HZr1v&h7v~ft)HDmkxr%0L*=CV%{l31`qH`bh&wX?8cpmj5A6R
z{&I=O at hQ?d=5eSo44u?BUhhjOn#TX^*|XiL2z_+oFq&@$vJbLByw>&TItj at YJBQ~w
zy8P#IqF2hsTrXd}dezv}^l7~hfpsH=h1T7>chOeg?c1n7)dT$g@@VyeB@=}Nb4g)A
zXjW;frks*`uIRhL&(D8AT;S2khdNZumoB}o)hJ-y=};?M;o at SlpU6=@i3K|jPLUk|
z7;V|0V}|B>GJ+x_y&xk;svPi811v0mSCNs7sF)b4I3gbbw};Ml19(EkZhhB18gwKC
zD&xbapB4}?W#n&`p$!&Lz3)m&xHdO8AH%=DdGqGaIKG=`CV0`$=xlNuYiraC8^`zR
z>9 at CMZwWfz2Y&#hdxnmV&X0)el6I*%63fmmF6f9VG%*x at UT-fFqO%%>j(1O^I&4Bh
zLN{(P)*pz=58mDvYxbsN7&9iU)9$kmU&waU^Z)p<9>TS&!sa<FA!D%%YTP;5IXNaD
zDzm~C5fPbPSoqQ3e<Q(p{bF!%@MOHcy*)RojG{r|4Gj%w+wk9M=YKBssN1`qK&buw
z^T!>y=F#$0M25VDj9r>fV<UK?Rqzo=`JfX~dwM)Re!Pl~0i>p*Yvp1yB6&_TX(FM~
zcdT3O$b~Qte131v84ERLGn&w~D<aRH(bgO+^8v9uP*A|8rKJUeB|dfPlwrIlrJbXi
z5eqf-8Td%(zUJJvECdOo^Jsy-P at 3+<fW5BXKx=f-d<pnZG~|<;n;RWI2}o>iZjQF1
zK#8kH>UAwRvauBqndxT}0fC`mj at aaaqAy<D0hP46H<OS8 at O10$vn!`gpH@;;MXWAY
zb#i=w{s=wXx;oLihK8rj!8B;ULPOu>Ecnkynwn35^mc(fgTT?-H%Zeh4Gs79;-l^n
z(0X;(6gaaT{Z$$&q=E#Wn}q(3&-Moc2Mn=xuu?k#!Gey%AR!@H{edm?_2<tkXrOlU
zek~Xbbh-?zDWh|LnU($%&@7|X-)EGRl;F=5U)Qa^st(ARtunPH%NAB!xbWxq at VA!v
z6Xn#}zq%_{pnXt_8`>2 at 9RRLJ`zUTv-919zmEo0;Cx=vy88MgXJ2d(kcq_RsnQ?x7
z?M#!Kz#~G3VNAAdZDb#5wz`|-#(4c@<c#5ZSZs1tw|C}M)g-*TcIq0r4NUzW<zXHG
z00o;l+6 at WJ-zY$BrBVtC0rtxy4EFQ{xqv*swx`@c_R?%PGsZ|59oRxTr4|$@1dt1<
z_h;Wp54RjCe<%~qoCE+J^x?xfLL#DWkZj;;<w9d)neAV1f<2oD$q?4~dT*jCzjsD0
zuK-duPRLDf at p?{ZNQemEtL0RH<&g038(fwnk<WfcJ_J*lmYkeSq-|+uBo=N{_k~q^
za9%npoK{FE0nC{M0FR-uv82As76C+*?Bb_v>gnU_W`lh!8k9kbAXrV{DfhLfu#(2i
zJNqj?BT;M10e3PvGm{ZIm5C-b^Mab+n(gwy1b+T}*J89P%G$=J{#u^2-P)~C?y>LR
zg`;`x7_n|!SXk`Cb0+XfsWb@*326qc3i{~*ct2J1vxcfM2hvfdUt;_4jp~U=NMyk_
z<CBuI3|RMB46nHsXz8vb<XDa!?C-1Z%InT1IIWGJh1?0VU;OnDTtq4eOhvF>Sr0wV
zeXk at YCRTy}G1;8$+Dte(^n}Er=jDxF-`J?XR;!nt9oui~=36u$k(o))!OotwwZE-9
zqq}|tx8M2p>}6vR=Y|#*a?#PzXWVV=?3xD$qs0A5q)>0)(r9%IK!!XI50A9YWi&L_
z3((BCHJqhSzI96aWecOmy7JUU5l5+u+Cf)c-8)JuDx-J}Yio|d;o(*t*3`JMm5~Y&
zA|fJuTwKbXO4SlqyCBBgm~1j~wXg;8k3?NyX2(qQd;-fzbu|xmASVpX&1GdGSk>sS
z*`;M0eU#)GkYv-Vx=lxyAw9Fvc5keSlU!_mv!z7~z62`~fQd08EiX at wmzOs*D2T#2
zJU*WFP?yk7&7%L6U3D%FLx$cL>#0kG#Kamz0fmL^u!to;U*T8h>jM%+MMt-g8t4W^
zMM=RUrqS2e at 9yoT27=ii_hkbJ(guCr8!1Ye1p2ZE>{)Y9PY7rsNezt%$DL(45asFR
z<@XCkoS&=3;c;<s&F!vDl#u!UQr#ob=ou(=-xPfAFhgbKG_Py;{CN-jzUIb8PfkwG
zrje1 at 8>~8#Acds08@}w$rUayxKBUI=?i7a%zwP4U($d<>bhqu~u;b)9CYr;@N4PaS
z!%y{IUS)}zSuo=O4ws9*bo?=!*gmmovj6L&eJ&RLnpp7mk`fZ0w6c*804t<pYIC!P
z$YAYl|9pL;6(B~?yEP#dwD7H^Y4}{`<sILJ=$8`>UF9z)ROfGnYRJ{)FFN>>@~O`k
zDi4n3D65gusE^^15Bp`YZRPIuUH>XIG(SHPAVf<=Mb$<2Ec+)28nh6YPV_o at z3<IU
zfD5f6!;4r}<So*Yl7S|A>&vydxy&4Q?j&{y9>{jB5~|4T?d{=1v;%b|!E)eXOuLfL
z$|VTK`I8A`TW{6<`R4KdS^=n4MI9ZIshJrSpriV_x=t|qSr8OPH7XkO8|w?`43 at 60
zF33p<OjBc{MBQvE6Dw;vBu1wn;oT;PLt9(hOwcm;*RLxkdDL+CsYhjIP4$9+(R%n0
z%VMNFNUOwDY=3{B^VPDBw459PA9Ivfg;1TAy<O|oOP44?ayag;CMnP4FmsiV*a=E7
z-1X0%wwv!|IQ0NrxJpZrs+_7S{@%esNPa#mm(^&j>+aw4(lRo5 at Qbnz*dMvpR8&-O
z+RiGTdhp at Hhn3Ac{><z*@$oxBl1izlgt(p at y?P&!=J!Z7sIoyfzNp^-L>MT^Pg?qx
z7AgbftvOj+nb=1*-C%NYF<DancGYgnkQ}zlBRoUpFR5JirXtX9FZ%qM3UKDsD6QbH
z!NIOiPkzh}Th*rNezEtC4%E{38XQZ`-A|i*|8RME*>-zTv!$ga`S8%W^3 at 92(ec5`
z<lnzZ?~DoPf+Xp=xx=rK+<ySNg6bm)Pm%1~U_e?(`>v)SDia_Oh#@TWF)3<Xr3u!R
z7^@lzAW}wV=3IL%R*i8b+atBDDp^r5vZ6wVtEYe0xM=^~+E`spv|H#`GB;;N4-;5E
zPQWRgpP&B%#=*&{l`{dra(#1?8e?c?CgX`so<2~ZucEGgBaYu8cz<g_<1b%#`^x8M
zZIl`s8WjU69Cm5H89x3CANl$D#ZT)m`S|!GgIH^6YfA%F90XniD{=YvmsWnHIedy!
zn9NT0;US^Zh<23f60?ouGzTOh`Y15H>2k!Eu3Sk)pKfnYpJ)uYykq?1$B&%20FWF;
zMoTNTC$s_r)zW1S&1;J5*4;pZ7ZXtEU=%@tgeq(FJ#*0*XeA1=o-YCUj)$2aDq(v6
z{=Jf=CF|7mw6d`=z1>{*drV+Nghtc~y`!$%>Ys<FzOu2Ulwm6aq(y>`yoiA?=^83A
zn}Y;GU^4>N*cf<{({a=I?|YqV*RE}XhgX!8^lH)dXcZnRd1Y>%`=X<iyFgnl>9>4p
z<;;D`sn{4s6}5!VoGs=G3^LBn{4NJeWrok5i9_m7PQ19k at _%{(CMFPJ;@jziFo?r+
zLbA0U9<_uq3D>#<lVK%7{)9$71F$&b4m^|8Sv18!)hCx at Y0(#*nr-*4Xy`vOBZ+na
z9kD%BXmH+cw(|`}5iI&_oj0DUj!y2Tk(5f!A!uC{W#!<&Pjlnr%1kdMeZ^$$j$SKI
zI)36<FU6YQKYD6cCpA=Zw*x2u$NiF>vyoeYw2 at tCq>F;H`Q7xI-p|t-A{aW8oRoW3
zqZg<qq$kG5g#iH4z<O%=`^|n?siDGfx*qOcO87`F6p!fy4aU|}g+J(a`usI&0|NsH
z^xy?2zpR*>Y<ivfx?fsGoJ|JuNnLh+zp~w$@6%fN8l<79cpb}X%=|U@^Qc-40G5`1
zDlV=t_p)Y*>~YkJU`h6+yu1?@NR3u0ue3!}jwK^M)Fip=o-{~9tD>SJN^o2IyAuHn
zJUp6<pC&2)BP3ppl)vmPG1Exv(9kq}$}{hZWJF(E>+j!j;(oW1LXEPrLqqZRm%prJ
zg4jvT$dG{8RrAGR>)pG>EHtK3pr=%3`TGI}Ng81hkudI+c`%zFfH;gno2(Se8r3CB
z_i`<-hZd4aNlB#w{XK7w&FY)-d8j+VT0q;MQnT3+wlmvB)eUR{D&KKq`eU!jyupc%
ztE=Ge@|Paqc4bhPj`3tu(B$GG^T_Q!AU-B0CZao!F$i3W?H1H8VPhBA5_9bRiz4-3
zzq(_*l%I#H>*&Z{ym--evI%D&9Gvmt?%L%1ytJdEqp)UaVPR5|h-Wv*ig#NYo}y at C
z0<2TAzT1&n`do#Mw?xpE#?*$jLyKiZs9$1sK>=IOCKeXf8TV*T6JZ7h1`f82%{>8-
zYDg- at +_O^WT)Z?d{Y8bSa6m53KL8iP0np;{<HXJ#g5T3Vr75jtZd8<%k|H7=*U0$k
z-BwLy!@v&Y2LTse!01+3r+t5(l$3PF9ZkJ_Kara+A7P(2wX&k{yV5SlHlC*O2hh-a
zLw8&dL)4gJi(&4xUbPanu|Cft_4wrB(#dG>e;EXVPV18wv2Ft#TuNZD)^!6YJ(b9@
z6Raj?Y at E5uVSD=YX}AGg-5O_zgGZ#d0Z^%_-_`7nU$%bn;`;JSpttB!t1<bGcmX^<
z(I{2*S^>9Xo&1H^XumY>taYAzej`gZ&HY%TG)X7v!AqAfBPFr<*OMHfa8zU at qBTRZ
zJk==a(nhD1Y6&qhvF~FMEZX82<NeJ!PN!}2Qx8s#R at _>1MSgs^dJ!Ywur8Wp)T$z$
z9itvMl+7g-w%`?A#U at 7WcA{aFy<nC3k(cuR{YdyM(q at y8T*IXnIcvvTEiG>1;^(le
z^uKFqt4=-2#cm8q9`NW3vLo+rJgCq13n?~x9#6Bul<-4oaEwU>M1uXlB7yE7k+8}w
zrA-;s=|jNO)YkUV at 8d@)ZS5GXV&jKY;(nmj9zPjsah2}C#uz<&*8MZ;ZWaiT)U-4j
zXV<lHvFJNbPL-Gs1{ar<pg-^Z^2&<WVcjsQoJ`P}-j*zUnFOTDhfjXQ_be~Cu`x2@
z2II>FY-?~VL70Gm5={PZl{gp_-c?dw)%i?!KKsQ)%ionj;ELzw=YzqAqqyFj at 5{L~
zoR__oySM at N{5pmMGJbzLt*VOObg+m9E3qd-L8Ed^dh|t^^^|m0LIkrugcevf6IfRe
zm`Zbpp;?nl(o;JOI9<&lbQj%!UKH}-bXL!O^blhVkJs8IfZgsoIk`fPdR{UxQAlYi
zH+;V`g2=Kl*AS;vl|p!!A26LALMO}vJJR!~y+wrqd6Dew?&<<o^^_h<&h`Ng+S1a}
zBEmY^d67}nesjI$=g-tU?Xuv20BjRu<A0pW@!_8B#<W}-i-;!UaJX6=U#bi3!f`|z
zOHe|B0)or!rQwh_Z?Fh&-BJWnnH$h|OMCnFt*Jp!nn3!j^(2qIp&Def)6O!Dlao_-
ze}8(GN=Co5F7<*vWpEx at V8%x#Ikl<mN^7dtqN%~L73FiSRs+GM<;6={1a^YlYHK7k
z3&lmDG at 1qVhME~}$9tZfTwGJq&ond!2M0H265N74JWivF1^>!GPcLbt at 0HTt_;_wF
zJvH@$RS@^Rdl^7TO(agj!l$kha-d;jNW7)X$4N&;MOo|rLsSqCa~mB(8vDoLTsOo@
zb?puo`*JiUA&0LLbEneEMRyh%wR+7iT_6Gf4E}@4v^(`8%cD<;-7)9Z^D}a{Qb54N
zCqiOkhzSV^gJ3;ClYI0i;Yse~4k}}AQn^>4UmG9IZNaE9_~$79_ at KIKzgtLSbiCg8
zA_iP|ez!@v+wzGdHLj1hcM=c`QoP2CzX}2N7^&q?C4$F=1t$k96M!b2MzHBmca=<1
zo4H*MT3mW+^!qMFa9%z`MJ<H_v359bakfo8sEzj>8M0BrWMpLDw;oIJ{RBq<-b+ef
zKf&3>g at Kkf{d-l8wwoFb5s@@xC^$Q+sPq}@v_}=$=EDr13q*<31lcrh_!fm$-T(UY
zr)>Hh$R~NstS7l^pczkE#|i}nMQT=7mLjgYT**M_ at BG~S*jC4pR^{|C!U)cJTj$WG
zmKI7(@24js?`sNXU#1)LRXQKM>djP|oR~;?F;b+b(5z)2`a469P>t2BrBWP)CWgRY
zF1hUBG^V1ml9A-Oon2+ISsvVc7b*wM at ynDRv_`N=fhPcuM9;$VvAerlQbxuX?hExE
zhmqa$*<^~apX;Up7fK-~C%3;;Hs&Q+<ERk5uh`$BQml9P-aWd+dCzqgW at bj`^d`Hb
zt%U&qv)8K+mhUfkz-<l{n}poClnQDEGC at 9lKa5?ouD-qtlDfI0Lm_jex~ggx!j5&r
zFD*MO*Of`Cn{|xLdEhw*<AH3{!1#J`b&JZBjgC<vy$7>Pdy#UgfLaJ#rSVit7y>~|
zrikaYVx!g at peOUfrtokEfBm|SskC2;UM+Jy+Q(Vl9$Y&**uhiWHWCDuron)%(|F>#
zE*L=9dNeuT0xGPPI3Sswv547ZVA-z3KePEjS#*8JO)b6h_`^^x6#$ah@$qqLPL9m<
z^mOr)A0LQh`tuiWIfA{%N=%oFkv at vgHL9V_C!W%D)m>my>0e8o-Fj?iXmNV==xLua
z??Y`um%f&kElS5S8waD%IAS`vxsL4U2<8F8DK#|- at 8ADaXXd5268>a*NV670j43Zk
zJ&4Ea0=1WOSalfi;lnvnUfWDr^YHNSGw#XB$?s}ev`hU!TmXrt{4_of*71xxGYbn-
zZbI>E_cH8TVVse^zP>r;&OndhdbO at BJC4!^Rg-n)PwRay9T>DlbDePqO`kntg-0uU
z8xy7%O3q{bPCS52clwDCDJdyX`SqpWUo=d=CloKGOHw6)=sDxAtfIp7^BxV&dkliI
z939ua`#L&0l87%#&7}Z_BOJdeQUZ%&0&8n)Wm_N1_WiD~@kBn$+TNZP1EESq#AnZp
zelv^Judv>wby~Zy-t(y5tA%~`%$W;4+17D-uA?ByZD-m^lQzC=itx4E*KbNLFOQT@
z5R`)?qyysa?&(PhqLS$DN|7*fye`Zfrh8r8E{T5WuGo&-Ua6>_9@*aRuBz5DxF5Y|
zPSU<&;oFhPtwB at 2f*J=uKhLt6X@^9Ug0D?I*jZuXBo}Kcikr+jZ)h?>pQ97TcCTe*
zY(?vw(O`a&;*5bxU%L{Qnt$tw<d+*cmT`d368`@FTiI>8fWwUe<QaO^j;I#K!^_*9
z;C8IJRH9~~GGp**(>X6U_X39J{(aTGXXEQH(=swH-;0($3H4VY^Snk%>EhxNefPP)
z&RC8=b0K}@Qugq3k8 at XpwG_Z#sO=sAHBS0#4{a9p2An;67JRdYkuGfqcTfg%lhe_I
z&Fwb_*g}Vj(-ss9+Y?9mL;pd*_<!sa+?gux)twHtg3L^>@$vE5)xSTAZReENcXsHo
zv9V1%6W at GNfNai^2q4>Rrc3Aq=QamE*YxfsWeX-Z64kyj<w17U8r at t!C))L1G^zef
zmCJo|$mL>}1N%z5g&PDnZb<7s*PQR{W_NUQirA}CN)b0Y9{c=U0dDFVc&@_kjmx*k
zxbm|1wuh~zq@%~J)m$pF^RH4`IQ6SmD1)Lj+HlkWyNZ9~hB!z&Dhx*eu0F>l^GA<v
z{l6+BT4k1UsPr(?ksx!VH at 9&BgqWv@`}ALkq*fjkSeb9}E}94yM_SYEXzM0c;%GG(
zRTm at nW|=N~+qnrUcJ?5|NWTgVE;X{WZiVt2NesHohbc`JrRSQZ-eA$Xe%pn5>C>k>
zr$$RZPtush=^bk^v9kJOI>F*G#l4zaQtB2$C%x<+?To$OPd&8luF=<Zp|c>%kdLDT
zvChH4fvXoyVO32h=R$85R!?1^mZJ{P<M;=hNN7ZagoA?vx=nOmC01gI*?{_PJmtU^
z4I9PGLjSy&xHwW)&A)y{Ub%YJq$QLg*)P9kJ|DnyACS>_zBkLz)KvQG at R?Z6&(FFF
z4I1hB__P<VpPgDL12+yq9^W}i%4Hh58pRRcoVj?{s5ESJs^$x0!bJA)hZW&)!I^pb
zq4;b$jeH#nH#awwJ%C$C at z1VJG;-C9fL_MKXw^9L;HZ(%eA&EwQN}+hO(p^t6U}87
zeuvyhAmDuH(q%>g3!_w*d}IFZY3-Pfh=D%d<n*C$KHL95<vI3wFGz--=Ls(2m6esq
zst=V~5KB?xrm?0R3Zh!i<fLJeY?xrbjG#v9&xjMH%}S-gjuUAWa(_Cpg7Z04X2k9*
zH*;=i1{Y6RSk;>4M{u-Hg(=xv#&Q<5%M8vq7aqj#{(2 at WkTs1*Vuy6=msFg+R<>G2
zQ&UC)0s<va!K4C?p@)ZuEwcJ*Q`+keAn0acWeG;-dkgf1E?v5Wx(k(_JfTPfF>}wY
zg)Ukyn$sK83Ag#Kc587kL`PRQ3;4unbVNNkSG}f)&AV}EC^}!ST6%A9&vvSXz_cg*
zDpq1a&AyKpYTy#ZWP&iUo$aJ>+?ppKz{l@|hx_isECeabhNJqHgQ8;Ki}lu)7Tn`_
z2!m(Oo}F<=Z+l9YvEu}Vgy3Oiu0OF$E{n?_F0;xIblDFCguku<Fkn1h=WRP)cQFfl
zqDqD;DZ5PKCO-akOm|;j9>?<hxG at M{WEkdO1qTOTczw{X+0d(IZqCf2SM^BpCpulC
zE$XhPu&{9GD3;3b=GN9l_YtczEIh~BkWSyeeM2z`){WD8Qu0(w%@%l#&Fpz)0DfM#
z<Erl);R7Lb3W$|y&CJYBJ<!wB+dNpQ^%WHUkjrQm(&VV5b8OYObTU7LOLLE&h|5&e
zX?rmV!WHS=-(Oz!Aiwiq(Eq7PVN*AmiY-qO315*H2r(c}9L)O0M#{oKLF<-_QJ at hY
zgBG@`c$c-BZbg>m==$QyD;AHmvIUUah}V at 1&S!)AOwW!>3DxzVs<;ti6o=#*1ejXQ
zgRFc6L~>CaV&!kpqRK4o;(PFtd!)@=cHi9Gq-S7|%HKMg&naL;ud~wDsr9idN1>8a
zem<+q$<dCrTUlj!+EJ*Y?XX%|2hbs^s99TE8-qqaQ?*1DxV<AE$J<QuN^MFqe7|r~
zF6Sx{XQEsTx00hHFDi~18`C4_vTQ-Apcz<JLqo}N_ihSg{+esS^Z(%S&{S*0MNElt
zS913mA<E?hCk<?J!PLaW+ggQB&%&?zSP8I9n^Y<lLOZ#%BzO1O&qUB&DlcAq5~hmk
zr%@v%o;uJrw7AJM5%0}!*OsGEfNK8)<5s^uQOL#fSA5r=V#*N#N<_8RV)H=`;8*h~
z2edD9p4|Dt=Vxun5TF<Dbus0%j%`J&_g%Sl$DLL_mVRSXCqFeIMy63imsoUY%rsjk
zYu%xu$bljIun1sY#O-j+^XJ$=PY)I5yU0-YG~H5_^8O*(!i(m2C>kjoP<4_UTx#ha
z)+F4i${Ctz at s}a8;}!H8-F{fRGwPI at l=N_>Jub)I(F9+h9p6GCLQrtJBSAr4ULKX4
zK&|R1G^^N~CM)O?{i{|%%oBl$y>km6UldpYZ56$F^F~-#{kO5MTAqrXo#UQap3uo*
z2sn=H+{@Bd0dah<a53v!TLi%*_De%*Pb$0d^H#2r at k=^7I`$@uUH0+w!%q(?8x2Vp
z3 at vGSs$3V&qTSTqo-aINZ*Pz423ccDla;RE08{hw@)VCW^s5~<KA;oR^7Hf0xbwbR
zmi=PClwF8}uXzL)E&KQH-_0M`LV;ht at PR=JOiH4-O2!|F4!>g4tGdorlRdA&##X?w
zGZwlgE3THH##PO40Y2*}O$q5&AiJV%9*vux$x96Esp;w6;8n83d~c+tq=<kEN(LQ1
z at N3<EWmFD>%HX1+_+(`An5O=I^~i><t{{veL>o at fDkTu`_Qf9h{lmBXE)Um3rHuF$
zD9rp?oNpB7_v5r!o^N$BZxs#)JYMjss}qeDbm28IGYhG%7FZhmobhwEcqO^W#n{*w
zRaCPAUocxmX>8l26eXO5rPq)s_~>A(OZ0QQGsfBso)F%%)QNg$Y%=*T8W$yeWR at GO
zcv<ACKDfDF8kduuQn&0hZV<!1l}e^y+l%#eIEIzxW*A&wn#0C)*K}J911qci{AN+C
z*QwK|(_CHmH%*YRjEas9E-kf>I+svW;~HfmGx|u4gG0&Qo_lU?t_zS;4sG<!Ei7EV
zd5+w5mjWvh#IEGWnaSo5qhr0`-Rap`Nm0 at G`1msq=3}JDg<NGH49a`Y_kXVJ`2G9$
zMGT8>rRuJ>)WnajuEL?18x0K&XA+0X)FJ>{#m)M2ar#RpCAySs7*l at Yoq)og`~Lni
zf<rd_npAgp3<dj32!G{Tu%1jh81{7e<*`}?)IeEcH5Tt)7T59lc}F^Wmmu%67(tcU
zsgd`WZ?+s(r~0$H{LBIITJu>1{FAJloJpM*4n7Xfn*2t%7%&bgpS|CQD}=&BLqomg
zFAbvkUa^WvNVIa1t0gwoo2%Wd{})e}hDxX~#jjRIFKwVVp0E<H0l*OYGB2@<MNqXC
zs)Q96bM8-uC?L at d+M6C6diqM=8O_p=<gEur_$(TIVKMX5EFH(kCyT!wbmWFlgjO}u
zy4`=Oe3*^CtD`zCJn&PjDUxENQFhvI2e+9YpCa_<4;sUGMv-!kG1;UG3`AU2Zf?7A
z8*=h??Q^l)grgmeDW0C6cLrx%wl&R`O8WK83J%l1<<69ddgJzHsnB^Zy{XEy8C5MY
z3r+4RWVT>8sZmi@#tmas^9g6x3~n9lGG7_1rMq|U9-~V76#`bBNb!$EVhZv6IGnV0
z?H-x|Oj??4- at d(3R#r|ut|>9?YvIZtS(mi*Clf#w%5l1%GaB2xPx^&$96EIMYBl at M
z=hoHLP3~8MSMGf|6YihNKrXhFDB!d``Fhllhps5~tY+&ld}WBg|20zn*LNBp(fl3L
zt#*4QRh;I=ngc==2LpNGn1Ar{B-XmhX~(}kUO)zowqZAD2#kv(4P({SiuDKSCUNcB
zHBN(uD?`;z_kh>fItXSt#kQ>(%8qZCEY$myKL_C7`19>Fl1^LmeKLoS1u&?Jzq2xS
zbA^skgibzI63j*#t8S%Yy$?bCfky75AV7S|<Jy3LfI8RIk(TwtQWLe0 at 88!exR^V1
zP1}YIm++W5JM=xIu1?RjO=}sLT3nB at D$0-pnTfDH-y$Oy-1)^_b_~y*JAt@(cnlzK
z<>UFKfOu&B4D{J}xz^NZYiV_lxt*}sEVg79FjVSErT%gntvH(Y at t481Yaz!b<G)^@
z%I~yoUJ*cLB^Xjh#?nHbo}P|m=IH3?o{x6)b||xn1fk1(aHh4EiYhS!Bcl(aTK1-l
z=9LHhZ}^-D1Bo?THy15f=;-L?JCi(A6CQ3F2pj+X{vH(*(3W&llUEHuL944~v&tx7
zkA$-j!-2>xBbFWNt`EiGaaB{OR8~=08SL%d%r at 6nSNHDO&q#_jinaCP1&ur{F$kbw
z9`WrTTMGkO`n9g84FNoV3gnxzt!;7V6ZWh4q@;2nXwdQ^GzQ4%A$L1`2y*2u{~5(@
zF1230YG4M11IDMa;+DhseK#UXm)N`+^(Ts2t%lV_hXiM;#skX)X;M+NBqSv4mYH>@
zUfkQ?m;PN75pFD2Pbuc32v?b>QxWV=j%|fUc=y>AEFoGBmy_c-Sy@>yC^H$>4 at cK7
z7j2%M+KLFHY*3LTPN8h1G&LnmT9g_p+IBOdvvBiZ48?&T&eyAtLBY{5F){Ii@~R<!
z7ph&8t(<nrVz_iE<H$&E!^iR-qvK%#^-y5Kqy_f0*buKf<0Rd-TWVA)i<t{wtxYw)
z05=DdSjWc at du{oHR;^d<d49N+E8ZKZf*>GnYmE8RNWBF3bj;~4u{i)<A%SS9Th9yP
z#BK1#_3ICKd3lxZB_{~E$zw9j2aAAaZrzf^%pUG-ppD8?4>A-IXepD6j9PIqj%(xZ
z>s}WLO+kdMWwg{57P4arog18;oAb7kHu|WcudkoI!qA2eeZpNe=nJtLt5LGHE_%IQ
z$f-b!do?l8Xnfieo80(lf6LI!ET^~OVU}{*1^2qJNl{Y4y}h4I8u`~T*@{VT>y`)!
z3IAzim^!}FaH(Zuro&{g$Y`97DuP8jyx}9U#7#E6-c0X7O|>lL_n6L!#sDM7gv_78
zy$pdzM9D2imAO~*4)xeX^=h1@;KLa{1Wc$3Ut%#F4sHN{v`{~A(&XoKKYjbcPDc(a
zMf6CNU7t-5Trds>yju&`4U>6;byt5*2Z`X+RBEkCTLvI0<-PT(=Eg>e)pe=p4QeZ%
zKs<*YHN8370)y6dr<r?KrH9Fye0gW&)V_!_aFDhR-3Zy{Lj}c_2)2T)9A0WVx*p)b
z=I-ubxU+if7~HqKGv9I>UG_H_!k9GtF^XlDzpY~*hYQ$u`Mlt%r;!n0{sJh2wx_`_
zHoIKGF?kxaGcvUmW8~&zqgT*MRF-;_24JU9NO1K^xM;WyrHoc!NtMgaGAY)sk^3XP
z-B%hbW4gE&3kfn(QaC~sd<*XcrOMs%AEOQV!Ozc|fBf)>jf=|y2sC{B__ll_613ku
z&i4S;p<?gTO2t>*;c7*_z638jL~)GTU@@}{8vV~4zNXxJcM&fWFlKybVQx+dcq$82
z%H;C0{KJP2@$v9BWSkvznW(G7$GG#dySln?3h)`O$|-1oNW0-y>cu%Qox7H$T&X;@
zU~TWya&N50B?Y3?=RC(pav{C!9RenGKO}Kx*@oYGcwF3FOY~eU(i{8u at uSJk@^6mD
z+}!=><b^O%pX!;oVXOREZg=YWU?KRYS-?VP+<{TT3b2~ir|&u}bR{1qfu;hlRh#-!
zb0R!lfJSCWNj#yFIy<%>e^A`<<X8ahKxV6E3BSKYkPN}ny*|}iZ-h#UM*a#z90F#V
zu~AV{My=sAuU_rw1jGp}Z6b8nS{bdj83~X)UY7 at uGablhd3if4cPgHGD7e2UxJ)O8
z-QXNn;tdup#TPH`(9qGTSX*<PJ%3(hW4i4krY(|vB4VexCx2zQY(>Gur+H>+xQx?k
zl%G+%l$r3>txk8W)3XpS!k+b<s?N^*U^tZ$1YJ;Z9n1`}nZ at 8S`8C2+Ll>vCRaNo9
z_`G)~XWFfHIZ&^X!!h8n9!}3JeeJsZC1;(Bl~1hL!f4K>-IsrhLCt!i;VRlDM4Jkb
zSj}y1ACr at l@$H?vqz99dlF-KIDOcCd!O?1``nk=a60<XhmAuJS6F2d at Zc>|k`Tw9=
zuH&bwBYn;0?y*E^qdLg_CQzrScLOX8hunj9dF6ctS=rRiPDRt<QWmU4fN$Tj=HThD
zE=$$TU45xy3Zdp<vY)=ib{Mr51=&gf$^Nnpz5%A%f%TCV;{QKr!;C*<FflQ)o$IDS
z_GxQrSi?D_Mk0VrXo+p*eI}T-Gl!1`QA_7I!Mv!#V&JA=mx>oGqI3wY93>tep0u1?
zT4*Rf2RC;*gGzcQNVyAv&Yxw5#EWOAUG&_1?qiFJMny%9XSAUch5Pe$nU1e@=UP%a
z@=ELF90HqtMEV at 0+~M4uD)1$zap!GbyZK(W)7GCG*p~FwlYN}FGg@(Q;&^O6U=&-!
zSvC&+d8Fjzj?%ir)Ibzr9opVVuS77#q at H`dAW$b;m4QgWkrVaDJ$3En1bYOzww}&x
zXfm5=9mQ%!b6zmv|MUWgFbNI#!sQRux(V^zzaI!Vp_Zxm=2=^`7?6vyTMc7K342U<
zIIT*`{e$hdk*O>`cgyx8i>&y43=Itf0|KzoKS$DZt~<>dn_1;URlCZ=u+t7{Om at +!
zEc=3}&ITzFl#-HCxBWn`m&Yo5h)X(X?ooefWbqi109USDX&M;NX!)Er2)H at hnItmK
z9aK`nwKP^+`(WYqeC+wpZp$9$u`>;ugU`6{Z!g7|4XcSpg%o>xA>R&iZgOQMp<qH1
z&zG`bufif?%1*N*iGM2PdWfA7i<Q`sqNYYt__>qi`J*o~$reX82ir?jw^s<-yvp6=
z37mm-zyJ8r+|dz$3N-*s<7$-d<o8-hNl4J9YO&b>Yl*&{_E3>@zvD~Sm8d3_ezez6
z?|ah(<k{rnqAUR3bs-#5%;2wIowHpj^#xO0FBIa<-WS^mnD`e+W9W^7OL<e7qy$~|
zMZX3R+$|#49)bup*RcNwy5t*`Lo+k!Pr1rI4*U=nMt~Kpq1m)VrqjRhC3jCsRC9~V
zNWME<H0X%g$MUpf^0mE*1h-=x>=t4H$KvnmUcq5iTgFPqojpA%Pkwx8Cb%19zQmx}
z<DY!cOQSSZoW96 at MO4(%((=r0{Seg+#-7jN^&r)5V;tm1f4T~aTJT|t0dz at rsDf*&
zdIkpQNJ&YDK0W!twnqOnxRg7c%b}G^=_ET<N-z054ruT72n*0sU at R5P2J*vr#A!99
zr7tawRH*Rhrvj4OF8*TwMxNfrsR5Bm%47ZR+*Kk`fKB8`<zl$grdlIps=w#(TQiP1
zZq7c$pvuGe06q_`NWbHkTss<*q2u68fSL at 0DNkmX_U_;`!89Mhm8P~fxx-KXNg|#t
zhk*`Kh`+xE6h2ShjNGEQps6ve_vtYfR^mlG+C$qb4vlQ|X2GTZB204QZIh><o46x#
zkMt>vw(n&=C44#4PP+DkU~zHLutq{I)F~Q*KmaRII*id<KZK@@Ddl(OEI*w$_avRe
zu!lp?wsX!?*Y0O6k*RW_wH11MZ3_-lG)C<oLrUZ*%y`1I32AJ{@MbI;dP5xjW;-zt
z+V6GgSG|6SY5Mi+(P8v#Gc}Lh{<Qji-*Z=uY9F0Bdv<EK=Ap+q91N;u_T}rw at V;7R
z9_<I$ldWCGy0^P~9Ye(b`i1|RVeN%DUOOL*A`qjuS6$rD- at kux4I-wTq(8 at b8J`><
zjf<xkyN^`bEu`){E2YqKa43&&gN_M-pYoRsrk%D+Lt)#?zhh>4lGl6gqeC;W65pIY
ze{4Mh|HZ{b-?NHW9a9NL%C7tN&v`)AGl%=sLJjZm2??dh$jA&03`9WNsTLVZS-*Ul
zl$dxsFETPNEc1}-?%fm+b<#&QBQ1T4!>Sds5g8(q{R?PA(`3FkOEq1V5LJXhM0a+j
zNc1^=Dxv0ylTn)BTxdH(iOLeKz4Yxh0tJ=tj5%ETu~;aR>Dr%F7gAejHR+M7OwLRe
zvv6{Tnwy(vDkceYT8`W=d1S<L;1FCw(v;)RWL4S1^m}q^u;_5qN17PuA{{smFKF_A
z;|uec34|B2##v8$X$K1Q|JhbS5SOF=u|H3nt`JYVtCt5`dVl28r#mm{_5vwHTq{hX
zT~4%0Os9w<a<}eCp;t>#>muA)tIHLWr%JZJDKnOes#q6zLerQ{etn|As!-SroQiD<
zzG28>CFH#O05jX2CZoK>_PzFt<H>TcvGz%<alM*?LI?lO)G<#V0UjQD`yCbaf<Aw~
ziyH81b1MY^_7MTWj}o#X6f$A}Iq^>Tn$3<K)CEPf3=G~NO-vvbIA8pEkgfSSefM>m
zOa$%u^XIMY><EO4*%oG`RX*TOS#emn4i{&OM{k6=xY@~SbO&!%%*W=d?p4P26yl57
z{%+&_D|nAe-0${-b?G4r3W_t5u6OMJi#B2dOy2lT9okAqZVvTfcxoNweD;giv8_q(
zS?bi#89)ChrM}*uBDQxL1K0XLcq4DWx1<sw8rBnC)Obf!Lc;gF5IL`{<nqL%TKaPq
z-+_Nyt{q0r+Fb>~I5dL<0kh-EI}_X<Q+3qhd5sizW}z=f<D^)@wTCsx{vY7!!k9^U
ztb?wTai5f0D=J+e7jy<=0g{5Fv1#3Ys)b;Fw3-j~RkTHOnSk344GyNXeI)CVmY%K*
zVzi>X+)KkGN558(MXQ*8%%W=%a3@~CNgOm9rGS9Inv1q(0Y%X$vC1=TouPx}Z0C=y
zbC32YWD-6X8)hkF6(MFUG&qm46!do1tCdmPg?^TbfLDLMVx79Jr_|luy&mCW+MD_C
zYe3jdtj-`R3D+UkmDl_x`)}8!6 at p8do;oKMvoks}Kedlm6sP~vMq_qzGsyYqYPOE}
zne*pE^72^V^1E06{-Eh!vkBIY#+U2n=H$e$rIm at +5dWey=- at BOw;ocp^sP$LsYZ=Q
z$b_nk+Cpqw4R6XSU*qBAR_<gJaYp8veNjXFe}M>3p_}#g*8t-FJgt&sR6LKlXGJd{
z5ckx;0FhR$>Q~XLt0JD)q+%DtwEyZP37<Xn04QQ}Z{xYNv~)7W`bqIy3H#wv3o4AE
zm6Za<7}j^(B6*jDgyaGSAZqefKw26NY9Xs}JCO&ur<$k5bqduuMMcqW-YM5BysKMV
zX-!R%(lRnBu#Ar2P#m0`R6ajzOWlv}HmBP!Y|(;Xu0IH8)zz9OU=$;WzI>B)a(@pH
zr?<#RW~j>HF4!8WY=fBrunhI5503X2*nB>IOaoX&ZVK!6>C>kjj}IN<8%;nJlHPye
zo}7}x0JkaVdMH^_Q!~za|K+%-_sW+Se@|iEe*XOVS+~0lhIlt`N(c%H_Ey at Nfp1P5
zulMbN)t~FlQX#shsjd#IMTb#M7QMK>xtTiCk<hcVGR6S8M<(PdJ32Z#e(^13MOe;Y
zSr#ZM1uCUz10c`1Q>RX40=u1Y&sNWqMsS+;BvZ<oHv*#W_Mb+dn3b{G3|Is7matl$
zmRh>_%k3qHKALbgefh5e8JCyGY7=mAap@^2-k{&~dTsoSyL#@UAe7MLDs_N{OFulZ
zIh}W3Ag9mA$G5*T=4QjsCLBD?->h|QQmrxUp6*nb<@&z4rt8XK`qeInJe7=VAm(Wr
z5he;)Wq1D<6X*7~7C;Q#d(SoKc5);EiigI)z<^OL8^5BWVya^3iEE}4S?Fd3Aj7!)
z3cEqWBvD3APEK7pGc)sKw&#@L^v@{Noeu;MzHroCm)vCLYB_A=pxbXlFQZvx+Q)e6
zLA?(_3RsaISU?6Yt}tO?;n at zM6KcxAc5h~rhyCl^JUmgD&O(F6RKc6w at j}PH)plM*
zh!XG5h>q5}DIf<0no(-&I^A-D3LD6CRJT4pJ|0&q6Wfyb)0oH)s29y`5#5qs$zq{N
zBUAkdXGgXuoC|Gv^<*f7L`2;Dc#B<_|EscU0|A5bJD^$_CXM`5Pi%5!aq)BYAmGru
z`yP~wOH1{Q;ptbxrbi~#w`a=aa!mdF{EQp at NtIU$uFWwPm_0%}XJByfadCwU3k!+j
z{y|a|=A!+H0HImXdz*iMoR?Nm$S5zrk9Gq=n&4snDPJ|>=w{@o<%nm<$0etxQlrfJ
z`t`v};+N}_%{(<7gM(2mp$t?n!(yuFb86CO9chA|t~fAdpRg5_TPL^d8NecrcXZ5+
z7VuBDJ^u%CQ(pZ2zUO%VGczD=w?D`JzY4F3TN(Id?r?7-G%`|Bzt%Mk64;ln7>uA!
zwx?f{HUi+2!wm%hOQ-k*8VlFJVtv>BFl>_hx5-}AT2D!@2^NJr1sls~wMvg#=K(RD
zaYx;H5CH(Fs2|hzXy2}=q=e>A<E6I2=Arxq9vj-eY&Dtb-AHqk8Hi0pL_`=h@=1=i
z2J{h-B>i|w#0tsSo*<+E*K<?<rfGk!6smNi<Bkq?E5fW$qZVcomEMc=MGUL&fz>Q8
zws4bD-6M)nVO|d|>EPiL>&@tWh<0HGogeXgH-G!~hE=bMe&Z$NIDP!PGwwP%I<AAy
zC!2!CmnthO at rj8Kdh8t<C!~6AQY+|(P&S;G{vVf_>G&ymzjkuTN^y&W^6uS`!=s}t
z>!}u0pakcX*xTF7KSswOM at vJaWNb{23O$&>l9H0wG0Y}<giGRL0ViRi#Q()&0U;CO
z|6*|j$3|uHbOVEFRb1|rs-;nAr;9>t8KH^-ztk6Zj1aAf?<F-UH at IWm6(QSMMehSc
z0X_NmpJQTT>UMZ}ct8`LfDp)VAs29 at 9;J1}&6#DRq!b?CIsM>wrQN}lO<P-=+k9^P
zohQQiGY^MW7~1TE0IGa<bSrI5;tzcPc|l-(bv3ZO8Z-{tv~xV#FX?Smr3T$W(vg%(
zM?S4C6}0)>$Y at K3F3`)<^E}pVH#fI5=X(v-8%GDPcpcWMK>!9o^k+hp^=2rL^SU0E
ztHx0bjV}xo^cEUOWVPy04xUTDo at EKp=c#Df=<ny3T46JzTwy&Wsh|+xxV<R#yggRQ
zx6gN-DZ_?A%Ypx33?!AIytZc9j9P at GloZY@bE*B$pFbaDkpylH(PalPjN24h$pA&5
z&b+87LbZIIC?r`M2XBUR&Uj0fTHh|<&99o1j^;JCOkinUi5n}XSaJy4-f5QCT~E)}
zO~DWIH?r_;-jB=8%{AVf?ZVx8F64Z;8+3#D(StPUu;lOWFZX0BrOY;m&|NwqvFq^Q
zaaji4)YR1Eh3O1sP_Z640mLx0v^;K|@gn;gIQ%MD?AbF~x!C(b;7pA{d~nq2M?b=M
zXzQ@}!dx(;MmCkfymx%WR|3v(V4}rZ-casxQD~W{=#3jUCYP7vd*;ID`D8%+nVu{U
znuwgldKr(a^wa6jd^nil{LvUdJ~vWfGdVXW1q7Tn=5`Wy<?7XoyDx3Zq(W#>zyJF7
zb~?z>hX*yO)$OgyZ0&jGfz$ELUtX=CmK)UXl+_YlwapG1obT1Lmykv at hibG<h_+ at t
zDwCOXCStY43uw<Dsj=&NOFCWcNw-a#A22d;Ws^FPaq<hMk?969N#nhSgWmbE=2x$F
z+?oYNURy=CuvHBfl4FVv$Ea at rU$@}r(g;rTXh(A9ac$Lji#Ov;K$WXLW^-*q!h0oX
z>k-dVLR{{aaq>wkIojEceNMR(No^5AvovY1H~Fs^BnlcIlH?&xQP0!DC68yGyI&DE
z5!+&$d?I7!>guXm^?EJgT+OuTkhMs+>)w(En$tQrH;04Hf+56d)?1cJUNVsjw`QTH
zp3DdvD>@-0CRRZOy~f})-GuOP at u`-uRvT*pmDt%?W7H!=z@`_AiV+QdgwihenGGlj
z2?-H)Mf2Kazg89o$TAr!HnA4^$y2t_3oDAEz}D6lz2n944+N=Sw^*xM{*trLC66;e
zi=(+88>W<?+*q>!=4Z`NT3cJ2*KspbmFcwaBTY^3jxG?Bu7fqtJN_T8{sW%N|9b<+
zg{&kiUfC<7%*;@Bc8DT-l)Va(><AG_GRqca7K)Hv(Xc{DqB2S-d(`io-k<OP@&EmL
zyg!dmgO~fhpXXfXy3X@?pNnH at 6_(O!QsT>jL;-ypLu<dmtlDY+pd4X|L$1Pk{Hb(Z
z9I~Rie@(5Q?_YEaB}v-6O{cA`UG3b<bL7Yo9>@TBDDUN4(|)3mfirXOA1XC!j!0F+
zU&<HG7Mo4$>$$egXK+Ov(rVr-DW(34fCqO*LD|bD*Szg_E2qv_r>+J*EWOZnw1c;-
zp-V7#^InXcmt;}7i4SQs8F9#1GbJTuE*<ZQn^94__a8Ee0q%{>wQ9#V8QJ$%*?<!P
z4=4l at a1)4=9`p0%Wnc5B-rVV#naNE}$5Ahhjg1LPOG^)}#d;2!J}?n_;qmR;x1%9S
zM2wuA1`tvAfk`FD#!})dk=C%V-G87c at oH*nJ}|4|nf?AfIVp+meVIinN_Sb$v72)s
zW3Bi=<q;Vs at o4bT1iaJ^3R1)c9xAsg3^7nq2wE4faqf+POT~sI6r>~vHN(D*`j8#@
zz3lS0|JQ5fF*Y{-chuLSR(NRti)WYSb@=)Dm*+=iqe3`4FZyh)df=J-6d~hl*irQR
zK8S}R&;=#B=+O~L$=T*`l7TV{S*TrE>m$Y$*2nNTVN_(~c6F%dSJ#uHCRM0z1-Gh{
zLjNG?DdIR5c=O&p4(zw%@dK~%hNO+BbHY!2>VWw^IQ at _b?$#Kf2hYBPKse{h==4cc
z--_qp!6;;WfU@@il>R-B3Y+Y)j~_$lvb1z`cqWDfa&#;>lUIFyZHDA|_{|M)Lru+4
z at Gm#FR4P1Xpx&2Ca{2v**e|-KeFd2f$1U!BQUE=`#}`@eJF80cOR=KPXu3ftPiIK*
z?CKj4kyj&3$twyT!<E>dd!I*5XKruL<1tblD=Z>{J8Jd*3nxv>Eschk-XGidZM6=t
zo6dkbQ_|^V!q)SEr3n0Yc$7$nQT|uhJnypF*Zl_23t#YUXlOVWm|a~ZI5+mS*6PmC
z at 42jZUT1$nDKW3RSh&8Gk(P%0p6p5?mgPP?Zss=&-G6>~EO7MbQSEKdqn|%n?S!uV
zsLl;Nsa`$es;nGllHI-Ilc8HZ2k`&Q1t`+MgVTqPNJ|6fXrG6mK at fl0-5uxosmaQg
z;Yp#0!}${C?u+lv0p?GV&zu+GTv=W&4%yipDAZ-ffPfX}fAQ!Wa2|ZS58p6M5j2We
zUS7s%!eRmJi}ApLjqbH1;D6KRxi4kP4qdo-F%Jr;cH{FNAb-CPy^+?->`QrIlX||y
z;QU9Whd<Wds6YR at JlijH=+L2|cPZx}Sn*s4UMR?s2S)j1cuJ}%WarR at zokQK@L5LB
zLw3$7?os-W<em4NJ~<e|q_O(<mGRpL7ysNWYBcIeu8uwTP?KTDY<3S3yJCQ0rzBUf
z-mO~g%uBa>?~BGBx-U(qQJoYK5m9V>|D;KQ79Dn*fyVa(nhQ!u7|l(+G%jM;u{j$A
zl9*T-B+I+s3c41aS|bK at J6*eW?V1ey1?d^9wR(K(NA<xnp$C at uCd*#V<9H<UWpi^F
zHep*^BTmE<R1C6 at rx22b=Qo45R+9h?EFb1P+TPe)Iea-F;9sYIcsQ*{Fa4I=f!4G<
zJd6A7O{V7aomJLdIiEtdZor>Xr at okJ|EfRvChKT25x#K%KiDlsc&!t6OACLd+<jm(
z#dvo5PT{+=2Udjqem<kwySEUtDj|UectGOnj}K}<`Y$(mO}58Q`UuT_R)r*^^U9X-
zFi&`q<fnK~Dp=*$vh*v~#DXcNFY6ceMR{Wwl{_uS|A<}QWQ`f9h{tzfR~9~HL(0E=
z`}XI3@{5iKXSbL|Ez=<QU4Skg6&2^w&oDjn%xDBScG~6MYuoC4SFRWu%^UNqbQ@%x
zl+w0H3VzSCk^WtyAgiJL<<^Dk1TO)%qX+d598~|rK!-Xzg@%qUt#5W_rup5weI#Vm
zwDOE)UnVDsIXO9TtUCt>$B{;S3k;`S>FH4elHF#^4b;}tQ=jcClmNhYAFfoH at lLxI
z7at$_{J9bu3<E*P2UNz%aA=1DctXX_!Jr({<?E98u}Gh_;mR8PRJzK?kK58D9B`K5
z%=hnymw%^SgtZ{Yapa)z;S0F6sFRYC(&YUP;iO#8kM%A6{W#7gqCGDbo(d7s++e|n
zN;?wZNn!7 at mG-bx8A}2x76V!<9y=xhL?pM(Xis{2I!@8UsZ|pbwq_O<4x7ufID-W8
zD3T`e70#eWQ<sLzzi}qZ%-kFmJN;pzpFe*tuC8{1ZjYjgqKw~~4(ZtO84-|_WPs?>
zFf$|I^vLPA>^SNe7}nMB at Lf>yqH*Tg-?^cReUM7IZEsT(Ih7_yYTPJjXy9LDy#dN`
z(x!SWJr0?1cXx09%OrG4ea8EIQMt!R0}dp?2}lo3E80IL?azwPG7YjibMMCx?gZkj
z*no{+g*~|vLKprR-fM4Hzb$A)4E!J_F>z?~cejiF<wtiqPCqbd=}I}g1e&g_Y{=W(
znB(lo3Q&^DM~_tdi}b79hRTVr-#nk1m4!nMOD*f at K@)mkxPUX&(5-{!hD#EVHyreX
zHW!()UWU&@f^n$Ut5=jj5LKh^pIK+Qub46DkMq}YyywDrW3fpIy`_~EMnuiR6%nnu
z;eB1_BYze99H3)ixh`Z}Gyz#fkZ~tKu`qgcPCK{l)a9(~+nRTOCl|5c2tkNHg9)v)
zYLlw?$%Z#+o0D{9dQ*)dJ1pVh;Y4Ad+oHR-|D1j7$j!@30uuO)ue0lS;F2$2zixk>
zE(uHIQVi&h-WE7<8Xy6E6(AV7`S?iDzzL+~<%rrlU0&(;GC|9=efq at NocOAdUExwY
zRQ?M~lN!q)9R!>o(($yy)5kHxM5vj=UqGQufTy2x@~%7%$ha^!AjGB=k|p3QNrWR}
zaiBZR)e>$=NlB>K%-Cp at XROJ6_%IHD6J2v~_76PQ&tJYon%B6ZofuR~dU<vJLeTGM
z`{3>GuetW~BONR2VPCCMqV0|8?0}`o-RO>$t7b<$^{39}!r!db-uLe{=0|EIoO`%R
zts7ZV#cWPkTC#z3jh`QVlCPgBgA-L?32>TLT1H0Rg{e-$(TjYw?jIs7o_KOgOP`*p
zNP5UDX5BnA#LTOipng~0&#*}EfSkPiBvgZ9t7kGeAVf2f<1?rU9O!|=j#lSKAATkE
z&|jU-4h5b^@4qm<U!Jju at 4vA02R7LdxI)$2+e at t3S<tbeb{VZd;^V1tD(iW&ZpzsH
zj9=$O;>4^QD6U+&Qg%0IFGM6iL?pm6-X)WWxd~BHRTkmE0UY9l=3WrI7nWy?%*@Rv
zL;r4Z4h at -wyNo@RP*hZ0oO^%RvhLz;AWH3!Y(|ZNemJupBKqsH;K8O&p~RvUtD!Or
zW}r~5pwoLwj0JEonzY*>+R4I7iuwlVX+MoO<nR;ab@%YVdG^PytWFG+7~}mzycC|<
z$3;s#E%4Q7db>UCfnmpy>$uYzXWA#q)o`m;{L#4Z_pv(3llK&bhAw=2dpB+mvnUrI
zpSn?Qi8MS6oa(QeDvFOt1x+4s9Qnq^=a)&m5UJ`<JU`-qQ{SIs=>jj0M5WB0Jt!c6
zp(rXUIyr>!aq}iEtlhV*bv?q_{_DB9+<<zga~SNeUcHI~;BaE}+pMGBetusCW)8*5
zANOEhH%fIKsTLZnuo3eW)Z1&-6m;OyrAq<Z>k_W-9~|_Ra~0y at A;w9kD=SeYB|<o8
zaB1pAzFztf)#%Ow at 9%>m5CA+PB6}f7G*U&aB57iU&Vpd?1^Fy&`P6X6Fs=D%=uf9H
zr56`(rKiV&#y4n~yaWLFbG$JQa!%F8#-?F%b>aD6QR at rOaE=&<(}@#gIC$x>X*n9b
z;2fsBygVEo<mTpvbI(M~tCi!JL<~#Ks)!RSxQg8}&#(aI<2b9!^sz)#hpd+T&K`Ya
z^QphD&#>OtrS$A$GSHg)W#ss!-*R@@xw*wbk#YF;Iz3I&DWT_cG3iOSF788d%p&HI
zz!I?UM38ih at 3Eu2-k)(%YqZ<|3Kk>}&arK(D6nq|{R{DM>EC$j>37)@E`5AY>+4%z
zzt)@SNxK8w at WsaOX*yckFf<Vved6WZU$hr(cYui at cB@7gKLQA3%NY0H`SylY*s7ip
z@;(fN36vrt0Re%TxnCJBM~#L;3`Kcws~LC2AU6FRt3&^SAM^87F8%zVg4Jd!O_(6m
zdTwtQXa^khSSfHl&xl2t8JpjV+7<+Pd3iyTVp_nBC<xOW5DTi&?S$@cuaX6Z>wE+*
zEsUcv4oEO!be_mQ9&FeUa3QzN;gEXL`M`7M_+-4s<I60b at NjeQ!U at Oy{c2EMHUe{Z
zLHRVSb~eIk)xCKdP>arn2YCXVIKIt_^%$+qwR(05ueBnp?A%-cZ*+_yFg93(Z}-X7
z#rZ{eBH&iZ_Oi6IU)W$|V7LmE%^pgNmX=oYO>Q-Dzd4hE(zC`hJ!TwDK^qaV3<CD=
z?j4)?^jryY`01ab7WhAWZ=@k;;}-c- at y@iYn3>erQU9M~bOvMP7EeS=%gQ=JK|F-t
zyLV630TgcYT?K#d*}j7~s(GlwMiXS#>({S)fVEb;3&+{={@+=E5A*;H>ToG`&d$X>
z at 6Mc_8!k$@6CF(sRDW at K*0?EzSzQ05>4 at v%M9VtxK?nkVU%`0FLy#lL|01ql-BbE5
z+vN!qK=ify{P}aSX~n&s&*CC7cCDoATU%3*Jli=&`%|*A;^DSiKYnD#VcF7dO#FIj
zM7+GbxoQsPpfdO4(90)YoC%yt37|xA3f0c$idrJaF?^J{YUlJEKw%O?5=3NYb3&>{
zMeSk6p`%J7!Yv^ae-0JB!ollC`6uw;M%u9}GQg-h;_AeUIs$NHtc&cu0xRWzzun13
zdkDn<S{wlm<kP6`;(0j*g|-hL4#1z7<rRxsaK=B##?Ldo`GQvUQV0U@$xC}Z?s$cS
zgrv!Mus}ed at ml%sDz-+TXCRE9Ui?Z0fd>$xr~f8Xb{`J*jAIrHXuV2|gX*A&!0I4`
z*I$~y4%g_zahe2@@&#Zia*x+(5+un21}7lAqG;lT%pkZxn at 3_8Ly1w1ZX-CbT#dm=
z$ndPknu1wny~f*NnQx}0QGgzn@*E=!4i4fZ`kESP_)fn5B}o9(h4Euc1wRm2z7JIh
zf?)pf<3|t3EJPJRkSX~xGJ31$5K6I-t9#gK99-w&rZM*5&o2&7NR%-_TDM#!pMs|)
zX!*1f*S6C0qdX3I<WvgzvU!N%_1ErH^x%Ocu<OZDfouIm%gMP9ejyHN#4$x>X0n3L
z2#0*6WM}C=MM+7C7Famf9sqx4b{6Mm|Ih58`WyG<c%e~v6v!@dnf9_sH3LYYcTaAc
zef<6$h!6Z=w5bQpJuyH3W=fyXe at YSO0avsBhXwH4FenZ`#_AYw{37V1-o`8b at dOEC
z8XB4)pG+ZrSJy+^J3BaW9VuyTZ7oi4XJcp2t*8)#s`q9G$O}Q%lTFm}X&5>#0l5(e
zHY+b&)GBJzM6IBpfM<S9gU?^QvbijT5ebDX7kGtgbiTw9NlC29z)y7=4)wzlqxD(&
z!v~dLzMKmP47C5+mD2P6eMf<IviRj+=NJ#4K_Dg8&yu6d&CSK3;1$Zv1P7hlz@>n_
zO#&ChzeK-2_#+Q+I{1Jsml_W at x9{2IPk^%Hr0hc7RBs?=Sn+L|g7BE6?LR+M1+)tT
zIz}9#cIGDNxCnSKk=L$Ku~Rv?iX1z}jtm$sQNm+HD!0u}PaJ(%;In9%m>}8pbdjcM
zYik3;iR{T07fHE!GYqgS2XwdG#V;`s4+nzQ7kA-1bATF#`6u?G!7J2M)##3DOLuoP
zmm&@g4aGTjMMXt8>7R^-Ee7<(h35(mRJ0r%G_ta?INMi7M#k*XIm(eIUShsx at m0jj
zKwtFI#CHi8+-tA2YeT#DkQp8a^J_S7G40Zn_Q>u?{B8AVP+HsD+m#-pGB|zj%$YOD
zoU`uUts{vLdjHIS0S9^qT)xbG{5VHKLIMt(1;|9^9#&l~1&n+O3Y+Q;7U_wvUva)0
zgqdse=qM{bh$mj3B59&Zj<T|{N;&rsjgOD3M&~5~@S*uODH+*y^0s~K@{A`=oB)1s
zk(QY`y2fn?N7e at h2BH}#&gMK-r~`3tj02sqFFM>5tbns}edXdMicZrrFwnu1zV{UE
zH`L){G&RZJKRB(He&qbkjEw8(cMF$&Q1^~H)FL}4C!91s0Fs$}(-n88b5Go0!P*8Q
zjTb(!qy{~Bp?m~@+W)86q9s&tdNW+rwbay9TtTBMdkuW9j~(C81Mw<^2ZgS?I0#hG
zv^)z|^e7T1pqJ=u2cLy53TQUN&g6hoT9uWFP_{x|wY0VpUnSeynxk?R7al69-uy?v
zCQnaKoSO@!dkM1WcK}ak_3LmFa*XwY!a_Toem?r7qK8|elZwIs$2HG)>Z(X|5{#b$
z6^4h}+S^M9kcy6?=C$szK(#*q+1WxnDuW(J9R^TH at JIAKf`924u5wt~TyX>5xJ2qv
zL<`R(3Lp?qap1H+J3G4|wJ2>7s8}F{Rij&^!jh92(C`dkBv-9h3$XeH+&LWwM?$T~
zsQuhPNwIm&Ato{Fco43X>>SBBpl=V8a5Kaa$eio&^*Edy2dV?^MbRXFHBLEnS{%w9
z2rF_SA)$X*ZQjw|P6||_6Qn|w!wVXm77xNn6PVW0_J$A6n1o^pLNpqLFiv*&^h}^X
zsDrawXBN}L4xK5D0rh4FtNKs<uyqcM6dUC)uFhM4&@`PfBrrgzTzhJBuPOpfO4Kzp
za$(_Bqg$(QCnoMgq63v<k<>%l(ZwYer*A at n2LMjZ4VJ}S{{4-cn_C5`+hLm~MMMO&
zsf9<16ZwOJf(#!!>PvZzv$4v!E4#aw_ShXMs?CsciD+(CMnfbJ$IjyoM{zvl%a>YE
zz~lGdJRZD>@q-puSy@><PrPh6oLUJE5a7H26<vv_%YU|yj*X2OLbdgsAK4GJBNDC@
zE$zO3je;zpVCU&Kba8RPF|Ckzy`^U*Aqvqf1;}7$o<`jIH)-RTfdLaVjD}JtXkIOd
znhy(S6xDuud8wikXh{gNU=KT4KM|DWPDmb<s<6z?plKpgQ<>o_D4Cg=ORb)rQ9Qfc
z5yz|={ella3Wpd&NsS2wyi_oGcxEq-D*pJ)KZ+*ll?!Ny_0 at USJHlsqfc{7nNuyQ)
zDS*R!k?ZQ*5sv-&@!1so59iwB413 at h=n#y|v(|$Z2j8QAS4YQgxDcSk2hk=AXOI5f
z{=LuM-ahh=c_|+5Mc6Q_v<-(mEgI}f6+`D%;j at n-IR`td`#@;l1oRDDnWJQ<>PJV?
zs~~xBeg)w4o}I0Q=UBs#lfJ#H0L9!m3<%pKTol5pjOSPk at RQGwE`ZDgqUNqHs-Hi9
zqA4iO75x;lg|~d7?&8-)oOKJ=hkk_edYuEOfOUC)?b?r)%4jHAW?qByZ`(UM0M#5y
ziJ*K at qiavPq!TTqCS2a<^`$BNA+YY9x3!{Uv}U-f!xIu{0Hkr~HOO=b2pxU>j?Zn;
zI1s)&RV+Vf;}?#ywY0Phpl30zhegM!xqx|UzP?iMnNixw{J9Xi0JBnh&d!2pEe_y}
zv$Y8dmxO)&HZE)GU4k0o+?%&s at z^o!afCtP%!jHuT<`a=C)fE#d%HT?QQ=H&pfgyL
zUuPXfLq8^YsXK9ti{8Lk9X>S$qZ3NV*1Rm1KA at pjQ&LiTK1&?<@!>H!V3!*F^l1e^
zt6Ko*Q}CA*R8*}ffLK}MuJ1qG+R~!3ZCdX;tC)z}tYgpo_d#GW{zZoq^++HJE&sE$
zuBp+0PrLlyV=ohd5QFgp2;w|ceisn9s22Daxvs7*7E_SB04iqT@)ssa>;1l8gJ+Yc
zmBa&oSKSP3ZE5kd8sw!uNY6W$w^l_mq}{HuNV}rZGR|g-ZfE?_f8PLEE4p52YHGq~
z-s6;eXL3?il^Bf&Ak3%6>U;#xloEgdNt}C0i`friN;Udr+6eA^LS?TESh6Q-hCG~@
zoFpb8AqnS&ypLR84*+Sr8|B>N$C76%tZx8S+XIC{j}odp1rcO3gB*P)!9jK6|H`$L
zzPdVTLt`Vllfk0zwyO7S0c^vBXgd4y_Rp2?L+H>^>-C9-2c3?vYM7Lq(g3`=rmdsX
zy1nrmjTW$VU~O#;VEv%SPD1)jQ_y8xOu#xOC~CX#QqX!9XQu-9mS-!&ap5?N86?Qs
z&yQCd8;=zkWG4VGTAE<o?+?Wny)MXU*pxvCLKV0Qkd^}_pxEL`IV`@A?`+?0uTM=H
zu=w%u at mN<o2dv=%B0t0#^$-ULu<<u;^cLx}E)}?Il$jp at Isj=O^H)_(4WeU at nwpxF
z>i`8>M%C6{D=Wjs8{@BZJ?ZU*(7$a-yqeyB{{-I_K6`4$n_z5##v}ld_l{j5la!PM
zYF65e-NUKQ8!_?mjBt|&5zWi3o}ue3EI!l5udjPXs$EEku!8`R$nUKV*d7rf?)aL3
zPa0%EJ6cDL6B}#8bD^N12m`W<j>Z6*4inAc1jPVxC`M4!Bx)Go+JObyLx^It@=U2&
zI9v%8ENy96mf~f4kouA3<)S#99b$-zo!&vO<^LnK^cZj$HvkTHhU<K$Z9P3{0MF1@
z`kyp^QLSudMgVn-<NI%m*>ErjoNR|sK&cI7g=OZ~*KV8`e%~M)#TGTIOuSdZ;~;H)
z{iviQI`k~JZVaSkr|Bocg#k*84z9zaqkGsB2Jz$-eliFQ9M=nv1x1b0hGhpG9jpdA
z)1kOn7;n4^;v6*@KFGIk6GGMYuRxi_Y3slhhaa7fgLLdn8+rBeC0;E&PVdzpXGzJ)
zc|i^AWtC~?4Bg&EPEKA8xYK;5He~h30aT(;$k7!CimH~bt{Q+;sJX{8m;R!Li$yHD
zCr at 5Ol?Fu~!u6Nh at dL@m5J{*Z;D&IH|G#uUAnz*(^%1>t0Wr;S@;d09R-h9-Wfrm!
z59X6VDR784whchv0WYzfxoNfwC_ at BDuEBDvxH}>i_M^2P#nz4WkR^BW0prlN at 6Vq<
zG1srF0s|C>Y+qjZl+7vJk`@ojLgGI|U7X&CCdTd_9w0|0&Jfu<I7ozS2mBa)vJY22
zI-2~`maX2^MVyG=?^&-(4thmzr7>`Y%4hm5uEpmsUq}Hakl2gaG)2PoAa_eiPmi5?
zk%;|Q9Quw<64+P*^h`S*EDv8nm70|m4^ZDSKF;AcHvr0WAG%iE%X#x_9;g==k4d at Z
zSqS!P;<B=u`6q68|N5d0_1$%T^a;-JzOeSw3VAv<Y(d`w#|j6uq+;jj#`=TE2=y0j
z?ps<~KykLF95zMZft;hq)dpsMJZPQE_o?t6A0MCpj3!uj?b`K^)46`$*;-p%T2jHr
zQIXyOG=S^w-UDR44Ps~U&(4*|s3_El&hW&;^?S&|0EN-7e7Mq%1V=IhSXwp)?w53Y
z9R_t9JIw&dxdBU)Xukb2iMJ|v>yl!?A`$4h-An at AoV>ieIPhFbit!(+0SUZQQX(|=
z at gp|GA|oRyK(Fuq_TjNp@!7{kJ$B+E7j`!Nh_F*gnh at y{2jr~UrJoG68}=p)dIotq
zJ3AK_7nK3RCMPG+a2D<mGy3QwxPY#d!~Zi{F{!X-hl^AOJi+#PmV$r6mMbCo6t3#R
zLgK`PEiGM!tXIM=5;87%dGnbbkg8|$)G2Wf1YI3~2=?Zvko>DjmH$XU8u6kIz`rSQ
z{gO=J_y`~tweBrI84SxUkNz{&)=c>F>U^nkK|z5nFdLl3k1gpsA1A#`nH#25=XLz1
zI`=L;V8dzc|Hj3}y4*xx6{}~L))zmM;K at 2)IX$(fb#<Sc!$|_R*Q<JREB}i9=b?c>
z>g(%^NT`<~70c;fZ~}t;0t64v(8rnC*y98mnuEqveT92iWZYlw^D^3n?&sI`vZA-p
z=E`^Mki?hVe*IbxTQKPMXlu*$bFA)aYt){$|NMv7rxKM6 at 1JDPZM!`Jv^vDxLqS-W
zIyN>|@KgZ>PLOwVJ8bQjIcol39ymFUJ0GmDK`Ux1?7lpN>_PT{&0_dW{CPU2|CP36
z&gB*=+`YLl*-oh#w2rN2CR|y#uIRo(U3AQY6=IO*<G=YonvA}tK<OdUi2qd(dtn1g
zN=odr0n5i*f;Gp!*W0&m8;&SC96`GpG at DE0RI>lv8ij0fd1VCwpy-^0gaq!7K<SqV
z{S$-(?f;RB4njA%_5Zu`QY1Rw6dZ at fFpvi*7`ImEad*-8```_Qzh>d<+Z&QH?!#dK
zo?P7AyHH8NlX&mCC~m9-0>G@^R|wS`06F>{g1DlW7cR29^m8m0o)XW|qnQ3=lzaDT
z0DRHX(Y0J<H_@}SWW#(yTQ-16$kP|`2P6%jcv?XnIRbdt3gyQeo*M=pls*IxxT at YN
z2hD1aQ6{K!O!9hVx08~>kud{lf9m(0X`lW1c$Y><+N`5KWZ~iAXgc>Ik<+M9XFqyJ
z0Zd=LdzT#weiV%{cVB=1(nJdhWaUKX4c4cZfAil{yiClaTI2fO*1>`I-qVkjc5TE~
z2eQ6;z&GM1Eip0i#LSE!iuOVvvUwppo9O)hkGe)n6zV{csH&2{{h at z#m!_W>o3-%G
zAeasU;lFy`(XsVEPZjjxL^{QYd!v<URC1wz6t7Vqh=3}K-r*ooZh+Y4uMse~XFL7&
zE*fZ{3-g21htbUofDe6e<P|$jtS??Xg7{+JK?Q=}7Pv29Ss7Fg^izf#Aj7u*$Oy}o
z)gPZF;^Ipr?xF9}$PED(W?KBM at p;@n_W4GQivggOckb0K?8Tf&V0)DuY!(jueJ|W#
zmEZR>1Qr(i?e(SgPk;ZsILswccx8Ep4!6JY8Uc9z{ry9SBZX!S0TSq%ygX|RRgi^`
zmzU&x*X@>w-Fw6 at OsYc_qz9da4Rf;^*Zt@)fQ}ka?$MT6UW%WWr}eIaKP?b!$SiI-
zIRacDI!Ljxu_5py(fy|$P0HvvIrmvuSfH9=7Pah2dlelo!C!{^dtY89<Jv=T5fPzd
zJv>4*Rsdc>$!6CQ8yAO)0E<3wJw)rOsw#0QDRp2 at 9fv(ehljmG?^AjE`E{V*<oco=
zFi8z0c)B4WA;`G?Y)o^$)zr&B7QD#_;eywT*bKME;<)<b!^}v{VJO47_wPsFzRj4H
zp56jAN$nanoxEaEZfjc`TEbsh`)T*;*1<Fh2eMD8=ae=e!%o~_iE?<6 at b9ms-I(sv
z6`lFt4jVQ_A&${10qMT`Fna4;@0WDAjO}TDgM0Dldx*LRs{Fq#Zp{C+g-_AR$qb-X
zD`E9Q4A~uAumArVaXKgnv5$IC_YULpOB2nzpg87S{MtnnhC=S;OA55Afd?H)l_F8}
z75ECCm~j8_Xt$VkLl|TcdJm$FWOK9f%wpP|00 at RN<(8D8p`o{hjLD4epX`A8xS?II
zH>++?lC2baoJGnx43Hn&U-+(TJDPn$bY+i_qMt3WDf1_uA|dm?r{AGEtl;SUbsh*V
z+VjOS2t?9EeMLVo0Prxl$8zgNDmOPb^k)YG69K3Rg#Rd7Gy<%mJv$0yG^HCDV4NSR
zX#>U=jy at d!8cHQgJUl!-4VQhe1^*&}eX3U2K`$TOqX54#YyeG`=H4H^Eo#NWF7Jy|
z#L-BD{-6#8dr?=w>W}n*zEwQyurPiM=yEPdo&UP{H35^g5Uf3)UGJjR89*T#@xd>Y
zhj*YRU4!dKr$9y9Oqa;wV!?P$C5~6GUST(bh>M?}47a=!IgYi at H@?eO$~VeC0kxm@
zh-J|(_2hQk4gwN6 at X&Oh&$rhtfFXuu=F;K$4jJE2e?2-!85A6B=i#x}%gbw^%)%5=
zWwhFb0iy%f$sWK{Ttb3Qp0D#969^kvCyXjPJ3C;S@>zH9e*W=8m_xIhko*IZ2YqnI
zpNA&G`k+@>(7t$yy{WfvM*_{m5`hZ at pkVaW+YTH2UzPjTdmvnhi155e#nQSVFiZ|b
zZ3{|t*~HfgYs9icI at 4(3)}x_@*aC;gT|fA#=o3Eh;IuACMJ6Z^QO}+!;D!>=KKsu2
z19-s3U+^(8b_?Yc6nFs)fImb&e0aDb at D<}BsM7iwQc13#MxS`)UzqCDNr-Vd0yhS7
zWdg!T9EgNG8-LWzo3sG|0TZCRu}=uqlFZ4;iQ`=1H#EOxWQ+v<l%*8P0d*EhcR<R#
zjK at e0Bo;(U5|Ti)^>!O7UtC%FO3o?XDb)ph1`kPO$z3337PD3d1V$59QLFkKKr9S}
z^78T?y9ZH_{2<cFao-0(2#<-t0%Nxr811%8-z7-ce|r}%S#HFL+qY978w2VJ7a9%V
zUv!C{fuXlRo8f=>7FJcYB(?=)Yti;((U}6Pvlj+S&zjY`r{BuA%P3cmrC$dzNu+FJ
zgU54mC-mW&GHeW?ac5;^C9RxJ(a(shSFw2)7DkkondyLDHd4;CPo~mLFrhRxH9^u@
z2PuV1`0eN5!2M%9GX>mP_1UF)w8#FR4>;(+G#wpnZ9Iij<^JIa5c~u1Os1pRj}+}j
zA4`bbma#E5xR-5jfld>~#C_-ECGM~r^qq+SIfFCVhJvZ7sj)eW at MCBaFX54T=gu{?
zXc{{Z%joZgdVXo at g~Q at R3(&9_NN*7pb*xi&#B36}Q^ndkJIR3QsYAM~pO7++`SRt9
zqJSV7DJhz{;Q=Y^r~=gNH}usT#qJ<Tn%91ud-m*U2w2Js<FU^;1hfO|5~{O8U1k99
zBmX;`QROxyN?>Jm`0_Fd3DqH!VlH22;cOrbc$@+aGO>lUXU|m-D7f;VsPlk4;czxR
zP`Wgy9W0-E?}DPx3AFa}{JhA8JQ)K+yZDl;K%_07hp_)gP1FMd<lyTOTc85wL+n_Z
z8s?;>5tejD^IoA>Jw$Npf5#3+2M3u>KQN(_x4faIj+K at i(i{^QxELek^uS4VbyBD~
zy-mSaAdJ at TQ<g{5u%pE#8i?-VJ*OvwhPl`GA6^8iWx4h&^zZS~(o!nHLq#R1YKx1D
zZLEjhzmF;}7lr%Wg@#RPpPGXAK*=hCDufp3P at 8an6_Fmhqww5*k#wh9jyGJs3IH<+
zC^Y;$RO!e3JP`e=AZ-xQK(#OZ7)Gmg;5nouWYl4CaeJT+aQOzrmlO<D*f^jOAy8PT
zYE_r!&42Pb>b2pKD0KP!_{<;wE}re^shOJu2l_>X6^FA+d>#i&jPl9wl}yXE%j=8W
z5GuyXbiij%`}#^Djllx}fTqU+mz&p at rtG}D#8&!e4s=(*(`S?O#)EH9{TDdoeP_Zz
z&HUP{UgQlwPZ^%q#E&1_-L;XCq`2AA)YSA3=M)tKI?fiE#4(F?LUBpDbBF)JGbqGp
zAuB8Ugg7DICGSj`c}rIpRi=ywy*wj-v_bZ9JOzd43yz2WiAP_bUteBrGoIf2YvZ>Q
zZmR)ov`6h>UYzOWMO&wl5pzWsSmHYZwA1}1#`p2W;PTIpTdDhNVkv+>w#Cr#_B?hn
z*p)ACa*Fi-&H|i^ud)f=UZ+!0QCWvP$0kR6XJ>0)A3c;=jr%9>+B!MCAlcZX<><%{
zvi;D#r@^4dBBP^uj~!!2-BD5N;pP^H63V$ZPc^!f4(=)XeD at tZg_z8*|8EoHEnFs^
zGD72az+Tcthltwzd|o2t%$2pZOya7Y1#1u$il#9*BX|)qgZO%k%OgPWZy9$$z$vR^
z-vS$5;z#a$1Qvt)Hh=$Y?c+Qiq^ze$4YZbyf#E8KedOj3Uq3&iQZoq*7m&VCuvB8{
z4~A?Uwr-Gv*u7 at oalPgBYidVF$GU{V_0S(5{mo3AMwAv8q-YCdXB)2$>?)OV(2S`f
zO at 5kjqw_r>IZaw3bNe-arv38!FVeMy{LbOLPU7JdQ5{WrEw8?AYBg5JZ)?$4-gZ;)
z^Akt^3MzLK71C^YpMG>&(f at M6DWQza%-+7fi28bYv`o#(ArcZ2(gL1uC>Yt;cuZAQ
zwG!~}(OX@`Cy+R3 at CQs2?JXB4<HU}z)2mQlhqc`X#fo;DzkdHl^ab>{?YO at GTpL{k
zp&2M@<%OveQnIoR at T~z;H)OST1XSYf8XFte*Pj2qQPH$X1wh^!PC~AAY?n&Bc){`8
zToe&AaZ)b(lEj{G(`N^-eh3q=WQxN+ at i`|aN!kM{v{M=&ub^&4!;+!DER?T~dcW_s
zuC86a&h2tMOmXM}8?>`BBzX968-(=|4zRx)7t4*iFAd9mx<s4%+O$ue>^xl25K&tz
zgI1j_EiHOc+VQ-*ckc$O{`aE;+>f1;ldK*cox=zCVHbcz-cC)GWT#qUtAF+kOJva2
z{F9emT at lZpD~(@SI7YgAclaAeNp=p27*{v9*=zGJUuxaHecQG*k`kNUpj7E-X^8;Y
zyz4BB)Y$#7`a<;<Hmf88kx`Jzg2x{A9bYGEo@${O85z;LrcFy4J~n0<85PB?sK^GI
zeZpG~t<ynlKxrpsWo0c=<NNb4KcA$eqy$wZpdY%%b#>`fesD*3%oi`BL*$MgP5QlN
zQ+;L46B#b6jC-^Hr(gkD_b9FyrPa25N~o)Fe>6amLsd<UJ4vhgv$wd at TQ<wOi&3+*
zl#}U4ft|?8GLxh{S`1JO+MK<Qwx0)L;t6i+LB~%S8)t6 at GsQ(!R*J(T*y~_Ix+wR~
zfBG#u<SXv>6ql9lPVJ6LN!c$fESz)yKKZd@#|Q+1vZ*Ob_MLLhD_5>?Gg9>q4w5Bl
zk-~rB%3<HWeG5q(96Uo2rCRr?qNwP^Up+rRKe#Ea!c*1N)j4HlH;yN?eQs`VC#|im
z^}clJT52jYke?HCM`Bb2l3$@Fz{aA|&T at a4S~sPA^{rRuV&dZ3KDR|<SJqpOtzr9D
zH#YW*^wL{>14t$EAf0W2{z3iO`L~M)kQq2<e$m>RqiN$CE8DTa+fqIZ?SlBycf;z4
z8)$YsW at dN({QCt$m07&O at eNX-Y62Dh5rh-}|8acl7QLB;#lAGTFBj|9=N?wz?mrn1
zFNMAE_nDbypySy5#Xj5k`1tVC3%tMqXf!l5irRQP!vL2jqnNGN+hS`ze*B2V1WQgv
z1`8hi1?m%3Q?nwjp%X8A<HijLTTLGyNjWwec+P0g2sGUmI67|lfnr4$=|`ixBuXu<
ztddev-Y+zTb=V_x2P{tPQcI$9&<7;Nf6L57N7|w`ZnBp7(I-_9HdxiqoH-M)(71+;
zZ at nzJq}iSrZbNJ91c-5U4UNxG;)_d4!mnSa0-iow<RL37+gJKGesXfM()+V2DJkg$
za7Bcc>}*axdP;~i+|jlP-AS1hr;H#t1zx!VdN&dH6Bdw;jt-=FhK96EqLwj7eZDDc
zXhhA5$2#Ho`M~@0TWbrv($f1?b7?_xMBcwI&^THP>#wJ$mqz11fVSP%SLUVhxEBES
z7iksR(7tC2P3X6p2P*tj$!+UI`zo`SFJEHCZlj}bfBd1&N8ry&Md;qAlA>`Pszye1
z!XhI7A{^6p|9<I-wWpS%*croZGar*FQ}id?_{wbA5I_fDADNUyx4p9?;qy&zeSN*T
zrNvImjRO!C(zvIuj~F8Q-aQTq(uj~3RUh1)U0qd;jTy|%%?*u>Ss$fQ*mJ>?kUsur
z-2ovbVEV3q8cj48nU$N{;geZ%;h^v6KzX5T!5+G)P4IsBUcG2dTHX^nI#fWEueP?T
z;!fh<?P;a0y@|^!D+rObo*%DQJpYrCs|Amky0EYib(D^-E~2Bbd9~J0AQ^v;|8H}C
z0_vNipiTrKTPs*lg_cx>TYp6DWq0=;fEJQ_-nzQFc8-pnhUZ9C;-z7IkPIbo9J>xA
zG4k3q+?82eU3HX~vZs!Xk8dwM`*;x`(QfVMM-q<y4<BONqW1y8Vt00SZU%bi at BXnq
zMUP70+E};P^LU7HRbeKQ082#%disbvcbHJ=!auY_!Pa(QkobXDxKC-j(nf_lwYx+1
zQ_xiq9FmL&BnANuX6EK5zgIl(%=Bt{<tx|D)z#hI2JjX=^&(O2?AZfeO&k2<AzK~<
z7M3IIEE17aR8%uX^Po<pWMouzb<v+5$trL;U}@<zKI+eaJbinYz3+H-VpbNLl$4Y*
zPg2h(rO-c$cZAQj+D5|tlSAO)F-9{p0-ntK_RUdFUf#YpPXk{QxGiM-GI)*vD0(w9
z^YD|=OP-#h5b+=gnx}sQUEV7uCKhnzikh);^6X8`(t-lM$jC@#3ky~d^r3S!q({=J
zf%WqYb&&SHKofZu7OHeT{?u&#tJh<Ciy9i(n3$MI2G0fr2J##|nx6YIriveg<LA$x
zcbeXQy@*F^tE;7sPz1-scGzR1kbED9x~eKM9$WRDJ%vY5I5;@|#m*oa&ktI7eSZAz
zc$+|WP^+&Tsl>Tkw{Cf_FWR9wJ$)>xSmQE1#M~Ym8=FVEf+`bBwmrB%5+~M#T at o73
z<J1NQ21LpxCXA3Lvkq$upAvy7OYT3ULVFecyuBJ%m at rHdk8e_BhyFPV;DoxAjg2~8
z?;79}mx98T=8v at ypDh1DX3kCidG2bbcO2+?c~QVpCKDgn*z?frmX{O}gxHbM(cZDK
z?bmNgPn9srdX6=(eF{y)qYT5t1nk7KdNu5_viiuKaEE}#NO+hvMx6)B7F1&RnzTPn
zT70b#Okr>v%3QRz^CLCu6Ib`yo;%lWJJ240wL#dT*1dUQ_WO5U9-fGks<iFi4*}Xh
zpN8#@I(_<dL0T5U1lR&vV}gcDr->pE1Nx%F at haSYQ4v3U0JYci9hrZX#krj}`PDf*
zJ`VgHTl2%iX4N${DxhRRLHhQ~e9$O;4qAGBa}%;)%_7^_)Kt^XjvJ~GA^s*;OjYrt
zNA$3~pvctVF+(V!xumS)=<u*OJCB53q$+n`U*FBFtau0|d^x4Gl$yx)zfMX0<<XBH
z*}J;BUXv+Cn4CFd4 at C9l+qa##@KA_W6~1k6{5E^y$qroSRiK;-(K>(tMqI3VpBFn-
zKm$`sb~ZL4$?|w(l)OOBAcUJ7DtzAg^&;v>_mS$@76`#F?@etwfEa$c=R}lKTpSZ0
zAAg at Fj{BaCfx$lPcp%Sv|NeczY+))M)?DA%NGei at 2snWUPUqe~!~)pos~LZJetsTR
zFgBUn+tr6hMka5_jyJ<s<levk0*Z9YQvf<7qLh at B0c#)qmli_*vKrq%Ss1NFoap0#
z{R(ey at wQ%i(xnD;kHuq7{r3%CKzTy$m1{^*VnW4{NDj%pz8-LdFOBOOWy!vS6cJp4
z1=?3A-VYA8LO~PFZTsxKxU=>txx*-e>kF_~)Z>pG-?V;s>=e2-47`Nx=tWZ8o4;^D
z0s<ei2z$9~G)`$e{GG`U9voWS2$GVP&V_QL$g%J6nbPKWN`I+y4PzRwtWA!3J=f6G
zB*$tJ`sa5xp8xB&W0+D0DvQTqM at HmrmvZy+cwk*$o=hgq#}hOl+culR-Z)|_8F-9?
z+!6ayELv#XAR9vKVC?TNi~iE^`S><K&~eU%hTk-IL at ZXL6}A8- at u)u9>)a5|PgCbn
zadpKb{b>6O&jCWMd0^nS=P(U5H6KYb(U0LO63*adqv!T+6HA^EaZDnu5YC;-i2Vlv
zpJr!gF&aVO!xiQ{cyJwXiU+$_Ou}Knru*&adDVl1kDWSo3ZVAOmvaE3VclO_U%iSQ
zcNv9>^7if9iHmi5(XWv<K`zpwJ2gHH927J34CtY_xVSP<8BnLR%*>h at W3d<JF;c7S
zJB(SY!(R+NuW0y1mB6kb)6hcKZiAM94Lhr&zG}M`1FUN5>sdB8H>ppYh(Cyk0F3V|
z*|RD$VLUJ7HdtyW{-_GF-VFupp+kojH@{and><?WI_D%W96?B__4<^6T#i}Pl1tye
z{V8yzD79!B?7BO9d1-2CQH%#n?R}jt87-AhH<h8s|IhFqc{?8W7ZOUc;I*wXGc+_r
z{TCDzM2Nr571MI_&Yf!qb?@Yqm(zgwp^fWUUK at 6o!OG at 8emS~@g3>;SdnK0?3a at fT
zb+jeaBYOvj*)QDDZ7LofA|DE)xhTxxH^jumHz6e_medcDkX#LUao*9<eqp>3**kpP
zpVcSN at ib$>(oMoC4-XGKp?Ezm?km$pGhrsIg4oo9ACD|^Jb2Bj?}r-oGq@@)MMcY>
z8y}c~rAI~WK62!UR+VT=8uqK;!!E9o{(F%O>Z|v;b~B&9e}eVy%J2XB^{cqF^m%zU
zdqR6hhaFr5?pgvjU;LKoWe<viWZZx#(SH!PL7?~~Wn}2Qz2Tq^fPg1VA at Cs40M<ES
zwejQ;+%WK`&(qVgKd%qNhwV{PQmTvi6P|VK3RCwT5!Rw&K>x!ayy)23 at q89wGjU2v
z3TkMei6B)c78d?Sw;5AJoH}#ngsg|a!Gqk82LLFBr%pxu_X@$xi-57%9U!7e``tY}
zF0B4IYi4FP0r}VgnZgCaXKLx6 at M#K4r<OR_Sy)g|;3Qi9&09`TRP<VU`UhG~4cd1V
zRaJBlU>DsUG&MB=)SZJ|6yBIt`Wp+$m-p-$Yx7h%6E`=voc~X at wxk>S($doSz(uVa
zV(<XVP`TAP_Dn9amTP-i7k|G~;yrTYmefbODIF&#0X)0|kDXgol!l+5A1~87bjOdt
z$_lDdMZBn>fYjRB+S$crR`%tMw$I9^Pw#*A>Q!ZJt)rl71Q%I12%t)@PffeX?;I8s
z#L{Z{jgcaP^1sSTq?NpSQvrlJhA$ujNFe0pgJl+a(HgYv6h_w8G at +rPCZ?up!c14Y
zzuH4mtiKEWLjoAUjV+z=#w&Ep%y`}xVfC<Sc_iqNwq6DYD_B(Qg#*5<xP^VaSSKze
zg)ew5y;WFH*42&66^vc_f#;DnzNY?Nl#|K4k(0v(&-gMSk0<6&MrJ0yz!NA-Bbpjl
zUoZb_T${Qc*fOx>$rqfV?V!<tnY4>PC@=5)v$M4{m8g_gQ?p<9j&KXyFBbUCw6||h
znwgvDL2_A&GUH*An3%AL2;!?r3~ITcj{O(LHO`&m1H{84%;V$M!yi828xTPL7JvU{
z?bp!J*VhNk2{Spg^O4_!DQ-82O-4pWWdj2*0xuthy_Bn~YwP=mrgTh9*MMv&3o_!7
zY5+!6C^ustKepIcrO_l4NT53Z`t at t(Zf)|dTQ}3wDXYw=;gbWOcpT5(=jF9<pJwKO
zF30hpn6KTbSf}xDLcQO2E?SD21s^#!ENt*sw&FSwws;oq_|^}}v3kEfmZEDVBqEm;
zIgsgt#NX!=NxC$IJu4$4qnlcMwvRuo&)(0j3|w(7YUAm=iCf)3wr76+B#UkfZ)!UJ
z=<(y++}svlXNR{Pam?6vbar#Y*FN#Zlzwiy_B5cYsO<0 at u#k|@x%FKVqWc*cC+=s5
zv_j#E2U3Ik_t<D at joQ=l^<ws{6JFWe+#Jw;j!9=qd*|Sl_og5<M~#0wK*_sy?HX_e
z{>&=cc0ndOy6}mKiI6seApFa{d-q}z69wAzDeVb2Hw`1WD2Ta0YMkVvRfeKyUqx`2
zVTG%2>q<GCP7}u!L&v40gj5OQ;nv}_-ku)iz(9E<6`UdKyI`I0RQ7n_cahf_(s7Je
z=-X8JlRqOJfub~%*%^+nA3$LubiSmRtlHcR#Fr1s%H~VrZZuEvSRGozcY5Jl-JYuQ
zQ5;oKQNb4*h-`tVK at 8c-@z|GAQBy|(*L68bxQ~6G&1ILMzdLJMy1FFU?_PA?V8xw}
zzZ=<qTi{_GlCALrjwN^b7hl~A!W3{bdU|?V1_mU3d<PGz2s5?y_0a=tET0_x_U)Tq
zw7fIcJrJf5Zyfi at 9c>WfNQ{Qdo#-pnZDV9tq0N5<$_b+mlyUvdgIruhz&!De|7qPM
zf&g4n(!F~LINxk&9>@_SHo!%{f`{>&QCJ<lXvLZO`g(j_3)gmKWo2hj_^dEf)8^b^
z6k)j(lo6Mqp0EFS{En81X?4KSWB2aew^LGX#O>PGKC=cg%ywn&{Y{<1iKPH%cXvLu
z(pZ(1H?p55`VEvcfOA0A0g*56F(Ose#$pUtgJ*%T(xUKc+F7*_JbYM^ok=DB=-s<_
z7k~Z2b{h{buQo4 at geNEzd?PG+A7|(M=#$)vip0|o&wK{P7l!SLKh{vefO`Qx(!=t%
zF-oXVm%{=AV&~-NPx&WKEd`(?hEIF(MV?0`-f#c@{rDoZq2TsMOS#N9oxu8V57sU|
zfvZ#O6sHb|T2L2uc+jM>Qe5!Rp}gGOT at M~S(2G{5C2hkK6Rms1Kon4$>2*3j&Mhg4
zm3AAP;QYHz{`&Q6!u8vP3hciFN%{MehXyjMu<+nTAD`^Yzl2q&@7%meYx(SwN3*J$
zXzCtdnIK$1Gzr@@u|X;585(lpIu#M{V|liJ`EBR at lXqLM?>|gPh#@>YzO!t+y*laz
zyxdWqF~SZH#5{J|2bF+m at 7}%DPoCsIer)`cz!VpSObPo0YDV;WGcz;IP;F>gS>tAA
zoFTMwL2N<ESX^7X?>|~wRdrxM$&^2N?{{T<r39N4PVeraV?%%<N8pt!+42+9a48gf
z_p;ZnLt#xXI3<MJmk?#xY!nmQXKrq8_SBo3NyI!79%0f-uzfpVh8pn3kJWc?-;M-|
zTWKbYUe7m1p(c6z__Re*GNjRL)TMyXdi(ZgiP}MkGi;GGY|cGwV>_S{FMRp(<@L!p
zG2HZr>Qg6e0?`iihg`w`=Q(lJhyWQ7))1`#TqgimMQxg1`)aile1P7$4_EF2TtEql
zZIU827UQ|^->U+a?7w{ZGBI4`5U|w2oK6$V)mdFyDh&F1DnIdfE1(fJQV6Ej)(P1m
zo0OUfY%2QtdmZ0oV4HfPgV`n$YQk6*$X0ys4N$!K>%}FY19l+hsWnd0hFUp03x at 2h
z3^~c)c`=2jHt{44REBGEN1))wnF- at TE{L4$*#O9!bB>O!Z{K>x at bXeb3`Eldn=8=X
z62sd8WFsZs1^3o7K2Ae~72MxlR~P5*A?d}VT6k;xC$okZiq2?g>;{~qqN2k6xq*QJ
zP*hq}O)(cV`IB>@@&Tuzja7&LW at cx@wfJr+{CqXUx-v4WYiykS$8WMd7QfBJ#N^tl
z*djIHkGTI&OW#YE`koE1bl6wr+`k{)*LMntsst_flBwalq1Dw(C`b>bsB+Q1+~G8y
zPHX}I_{#M6C>ss@^kzr}D6F7-A(hvzgzPJXWW~oDSmf}*T?RF;-}k}27fjl{XXodm
z3<V<%1=WGefKrF50=4Z+!uKCP)J;uO$A<Xz(s~wxbzu$IXvl$lBR7LcGC6%Z`(f4d
z1$(k6*LouplXg5)eeM;#u;o)wqb#8t at 3>=}R2rwBR97n>O35oL+uJ;)abfQGu7HLq
z=9HH^UsG*14~FbgT&Yp{c$g>*NW`(%QSzp=>NZcjK6Pdt^#QF;1ZYy!#(g4eZq7CG
z+BF^ti3`o2`H3n+|Av&>JP%3Z-EtUMsewzkuwV_ot9oFDC8~Zk$8e!+pr+>f^{z^5
zi$pRjD=RG>9aZ4<_V)I8KmwIhb2HI at JmP9ag%}$<d-nHtA3$0l^LNPzTwmlyQiq3q
zfQ~{4A4!kr(7|;8-4qcK!Oi)DmywE_DXw>9<dA=&mX-0cbdR*G_ at eYxP=JjLDc7!%
zLA>SU<@GJxOXiCCgEeS=L>f1`6cyP3B7pv#08xVHs(rrcJtEmJ;fima0Wn<W6%wKp
z5fK6CMqEryWnK?D4m@~bW=3v~Oia|GmO53;CS=Y}WHBJ%)5`ZD36w|i?zW*a+3S3_
z4EXMuYHL%X9?I8DjOeiMtzbWR at Sw?=Gc+Uh5O}!PAH1+yx{)N1J!KLSxd|b%pF!Xx
zpF(+p8U$%^MTI8BIG%j}@xwi6xmVNM-=BOj^c_18EVyuNg95=){}N8=xU#&QQ&K_+
zWC=9Z{dbMQ3l&@69^ngyxKBSi%G%r8%O|T815AdFjg1;8WK0aj at aU){JH1N$o~_mS
zHid--+-B3uK7M!Bo5EflcoFWjV=Do^6n+-I<Y at Nnk_a0c8_~-OS;NeS+rPTScpp_2
z0YfY+D|7bn&~S3PZ|Mn8s_SX%^C|0q-`)a<e7sa==lFU*?#Z?HLqlz2V_6|I-gTCB
zHDTcZV+ipA2KU&iRk}@f#7_Sng$%DU8xwn{dq7ZWwc4C;NU3vbX(p=z2&t8T9_^G0
z&}AMT9`B!HN0FQW=3!HEcJ{zx07oLZtgP(VGyjFUi&i{IF~|S<`uRc5r*I_vId}5e
z!bWtL-NTZS-f7MUWC>g+<ma+ZSUDKIyYk>kp;(d at X|7>R%3)LD0837tGOsm_ua}Db
z^kK#H(!|5Fx+&}<^9M7wRw<2YCT}-QpHOZ}*t7nejR2w#QV=vc#F#1>{|$+$dhKU$
z+ch;c#U&*wAj5H+a9}_^bZpHhNo!ys at 8Z+q;$meZBRU`$hQ`KM at 7!U+J(>J`wb0VS
z!rhQ)Se}N5380d`bm+uWz|+O=fbf89?in7AfehVrm@^GmR%X6HEe#L2K0q6y3Gfk{
zA$ZTQChY9&1Ofp+2MXI&^VdPYzv*MIRG4YsaL&gej8{Bcwe>jGNr0BZ at a$QkwFVD&
zclF at l#<6MQ at G$Fb4w?ZU)6=I at LdxMvA=V6j`Zzc^q-A9MDQs;>7BIk&c;{OVL}FRB
zT$zh2Dva0A!GRaZzTxT98q9 at KGBSC<0zyQMqYlAcEw`?F=P<DjynmmYmq!ej2Nkh@
zp<`rZWVz->QW9N!d^~PUjf|MXb at wRc-@i`|AB6`7akJy7&$pXAER*Rm>OA-fO-&qc
z-n at ZN$mfI<rKKRfXyfMQmaNNl?5uMa-`uxGFCHB$tHw`i at av}!d<jGdj at T%$yFg7B
ztB&1Ae8-2L9>o#G&D2zKC#UI<SMyE4$WUqm;pV<k2&@1C03Lo$MVXtcD|}ZN01Dy*
ztjY3~#f6S-#p3qkDHKxT;yD!$OA-b2Ge{*RCDSr96JxCe{!&+&VPCPmvlGv;;=7E1
z?;y?`-)2#QLSNt7YJ2sH(p_dNJ)}sTCpRt_H(uD}yu+#{H$WZYW)2b$OG~!KwkPU5
z;ZL5(qG`+8nm?>D1Q}>!V3q2+y4?%M<>lpj1_xzqjk~<_=*M^4ILG|oSpeW at 7-vOA
z-xiKjQ&a!9huG-!CzL;?SDhzm0*D^A4Usd2D~Aag7jg0Nk%Br4{Mo`tDshW(|9);j
zLfqU2^ak|+zj8o+nVOmg1O`UDo4)#Px~-+f&TxE9X?sx(UxNb;`R*_Cdd1e-0(RU&
zpL)J>eB3g%1YDkklQUao<FWSfB*HvgSf$&LD4x3j;c}{kaiM8T6j<+Q$csz9zW6iV
zzkd%nc>gyy1o_2m<Bg<nGHUAGfOGXZ$z9Q~rmw!PE-x?Jy1P at O@AmMqR&74|FVoY!
z0s`b38XC(e6X9D)XdVVK>FDV6^!A3=)g3J at E1SjBk3Z(;UrpSV0M5#F`0$>Njt)T)
z5e>lXuCA_`te?vIoT1ksx9_^uR95D24h|0DMrlUI2-l8&XD#p~&^>!rS66Yv3 at TX~
z(x<)Yde=am0|DTcluXXrqL`$FHNyRUd`ES1(jHGG;%O0b&LDBy<e8 at 6t;NL9Z3i=Z
zH!m;jTX%JL8=XEK1y#0we7ZMZ)7jIrh}DKX?)r5q#2BdEkb;B*2U>c1ZvGh_8DRk(
zRFHn<whL5)OP6GJv<wV1PoAVX)~uzXvJ0MQV`Jm;!sV^`Csj`GI5vL+S*i027x1xx
z<hhvbf9X=!`+o6Uug3UWx9IOK^!4?zi0?a5#!EndZJYMugG_OF{$dZa=mc;IfN?^l
zUFI7{2dEgskIw5p&+Z)@yaocK>uxB=vB1c#E}enF!M^G2mLLy<EIB%atG>R7*jP$f
zgjpY2zdu#g)qHQp+H+=SW)RO at -P}--4xQR}djYZ%DKg^NvuDxLCm-Y*TUoKA4RH6Y
zV){bw`}a|x#p7?>cx5i9vaS8!Mvv;*vj^^y6uC09valFhSj2U9o~W;|FW!nQ{%!_Q
zQvC3tnvG5N+h8Nk?{jml?d_zFj*jOXD(F04%eVj?$|)!yg#^cC_w$nu{;YNP+?I9Z
zuK>k5hT&uZsku&lyaax|oXju4y_97OkJVWif2wF)8^3}tmq2EayU*lYPb`Vhe-My7
zBJltK0}2vxI1z%AyE|LuVt$2oGJo%lB2e_=Qc{s6B|@d8rM50El)Sw8Dw!cC;%$x{
zI|gD(71oPt&mJQqBisgt00NLy^)!_{Bymz&eN7|_GC1G=S;)m{w+Bnb6#_P11}7);
zE}5`8JN<+L0;otqN?czF1@~lmPh{YYM`pr!c<A+O>Y}0|aR~`jTkakn_{KQ{L$cGK
zC-nhKJh)X}VzL?ihU~w-&r at Kcl7E9HKllN>R94bPMMVVw13Ps1u+iDG+5$^k)_OvR
z4t4EcH{>*aXL0`g`Fgn+d0E4E?*#nAbND|kA7rHZ%5<zm=+=ObnIK~y33c&wxuIZ?
z>r}uepG*MGU=>Gudtzf_V>1hjw!uNB+S=Nk41Ih}2{?9LK=IS9lY>BJJHEV1{yaBF
z#mvkscl2oZyXoZ4VLSfhS5Q?Bnl``}L_wgLn40E4dUVQDRdmI&C?k7IM$I^uc`i^B
zR^4;jjcA9z#z=oK_*mWW$wqRLo1^I#T(muVmItm#y;nXd5 at ERaOE9y1@w6E*ZP0X<
zR#u<Ce~*KT(DwE%O=oB4ml4M-hPV#jyho27(L<cI0)^ZBs4Ocb^|JAJ*ozx)gYTa(
zzVRCB)AEa8fu&OlrbY`J5bm~*KP>;8AE<6?@*HoVB*K;*dHi1&jk4f9&nzq~xKnh1
zsOn at NF1C8Mb&}`jsO3p%g;Narqa!1m9}_`T^v3xQ_+^xrivmvD+S(E^FfgR0r2$C}
z6OKqE9v>eEqTB{}joW6saPr&e6R*FXeeoUkDk>^=hAd|zRk?rt`gQZ}U45H;!$i$Q
zj@}nW3kn7 at T5G|73okVl6=~8ek8ggF?n+)L9Jm4rcNo5- at ok?!LG#mz3v*LVYpG;p
zWa6^2(RFoomUiWhjXO^XCIh3RxG#!zjGv!uX=w at AUKE7g$Ljg%X>wE3q5Dib9WP$s
zNFpMRPc{TrR(u0>;`c_|hUx5mAT at M!bodrSYilbJ5lS^6`m^CaD=RC=kvu#^?#BF#
z%WzffL7&7w$NhH3{2wUQ^w4a7Do+dE>-$E(W2d00Kf}V at eC9jx+;=GF%hFesxd at dp
z*8-QV9#34X>*?>m3h;pCt)jx9Lh at 8a+>+euFv8t)M-G69^WI!`$x`sAfuFdzx_Sa+
zDaJl8FK>|=UyNNZ++IV2VnIOxm$dZ$l9CcU(;=^*fIW- at l8!hstf{GKadB~7H|puY
z=4;$`w|aKz*xGkp{$$*WgpYmas|ZvOQukJJvYm&=&a)Q@)j{?)eA!ymwVRuPKgJtb
zKL!2X8}?$T3BFoTOic6q`GcV0zLfi4R=j5!Qs9*kIkG#_+_Z?Ln4DKhb-r9fI>Pw-
z%V3`?1BT9XQ-ExBTiDYHJk~un#s-`Y4-+>w9upQ3dF35pHwfIMxLA;nkB{r%!QBTA
z8~}w2%H`tT_fR1&zB at X1Z!S)(sX2jNVP;F<5VJ~IqKvF;{~Hp#n8SzFpz1Y>f{v27
zFsc6Z*|WC(eun=02AaysL at sRXpa1K`;p?n;&;~vnzU8VO|5d0O!otEcH7pRPGjnr&
zN5$a+H`%CPE|~W{c_{-53(&B<yxce3GdTFzU0G51NiHrz at Mv4?t8DG;b^(WYT33h1
z$kXKg=AN3dZc0M<PAn|!BPS=fYz$0cq+%c?;W!>dAs`@tZ+M3O-TIVH6U}vkuf*i~
z%a>Z)>r;ui?Mh_ZylY;VX%9{T!rJN_dg;m)g(F9f;7OjbG0Xp*&;yONoPE@#kFVb7
zhO2phUtivLLU9HeKv|%ZT~6r9Trbh^da^kSN#)=%Algv<v4K!u&w}5$$hv#?ZZmW9
z*70$U{~eo(jwU}4Aw>oM%B`TlN=ZqnrLV7X<_r^5L-kLo7H7|fzj4e51pYLV^x(nV
z!v90ndw})aw*TWwOFK~!8d}m+N_$GQG-zmO(a_#IG_ at pBT1p!YiP8`acapYL+B at x~
zlKju}zMt=L{QmdxJcr|;KA-pdy3X@;zFyaL4(h!tIyw)Y=RweW`qOTwh=%gD+DyVv
z(z?GdX`~~4HFbYl=%sy0kn)1aOr7*MYt=%p-glS at qi1hAnD_d|hPj>HrC~Q<$oDsd
zEtC^3<zV6(_KKPMa^&IT$1M&Y8!SqlC;p+svp+dG7Qpa-+S)Ea90S<;k-(+#fcbkS
zxJX9Oj%@yu&b+iBl78!|C7*Cf8el3N8v)(h;r)*uwoHPr5_)=imXwv<efA8*vh>Kr
zg!0 at sG6|q+larGGRthdI!a(6>ejc6cpK2a-EMPmkf6^C1*Y5 at A!M8q7fGB(nIq+w0
zvY^-ml-j-$=h2UWHT-pdO%k-LmVx25-wpb`_<gXWL-_hhiJo))k3qrw at OV~QDhy%X
z9Af0@)2BghJm{qoUPM*%^l7l{w04aHY*+q^7Y~1GJoV+x-#K5{8#9;l)OSEnPj4oW
z;_2UVhhevq!GZNB at _$|_^o*LL$nu<V1bGT-dXQOpqXJZDivu~HUIJ(IT|46D=7!0B
zfc(e~PEv=(%yBuytA>EtSlzt&tfb_=Ko-1iCjCEpqud^y!Vt!R(!gHKno&_whDp1y
zAB+t1_j<Ru=m;FGy(=pJlyA3Q4G?(PkS_7!d-v{@ynda{(tXJG=FOYxPeg51Ejd*(
zhK5Whrl#<^Swcbr_b4z>7MN%IzyJkxxRUrC9j6_zob~#K1}nXVb^|lBI6Ujo(|cN7
zo$4rC42lq331ZGZX at RUauuCsrqBzIijWjeg0KqMPs?7qoiLqQ#o$I%5)y;{CiTi1O
z=}PDrA|hDd27lA8(RcXpVLYLBt#obEol{tN4QyI^jw?&IDp+4nuf26TAmcs!$jrh5
zaY}0#5SYP|mzkNj1#7ny7jJ?p_y7x+o}RwG{h_Zfd8jdo>BQjR;6KgHSnPX0BSTBv
znvsE_Q+yd7)phBu9vi0zmFSbaK%Fkb=Y at r||E2eW;>DA-Kuz745m*D)Dofmv#zx|=
zU%zJ0I7Z{Yo9zdxw*LFr*`%jMMX`>&W3H9%W7$hZk`S<f*vD68e(UZ&me@;HzAST)
zp(a0N_jYcbcD)>k*O`khdKR&*H at bA03P{g3g!wMSXPus^+4y$tpVrp%dU}VtGHEHJ
z{>!{dpEaNk-+$#w&rLfkf)m`^<mBY{A3h8Lz7NK5Qc#cv)?(iYL1~?zQ~}JX!1KT(
zCCBn4-;|ekM3v>_<oqi-P<-^?OHMw5TQV_01YE}3&kxvzqdNC2=fMNmxZ1v~qT-zB
z*|Q4ZoLEJik-_305HLDBdr?p}XuabC4*Lxd$gbVn=rtSv^J|#pO^_g<-m6z<CMG94
z?2mT;b at B0$FnRc4)Olv at c+YF)muyEtIUlfda99wQcu@#qp}f3&J$4l6-1>6LyLXcB
zTwX#*+mq=ImYUme1P23 at Jtrxtc<oxshxb$pi0pV5js;SytFA!4*hEDSWoBl!xqm$w
zvGVloTgJY=zKEzOrCYc7#>U1j>+6%kYG6gL?0Y{S9|djg6|bPHK<s6HJp43%yJZrq
z)xTH!teKf+*xK0C1?{b|C||e$#O;A8Mb=_5=~2~&Nmp|#tB{EaD~Ks^PyEN6S+^Tj
zu!HE3aMZiK9CB at rg9i-w@Q7q&W=3^-9ihR_jvQPUR0@@Ww9JFE5XjnwYb74kX6RP3
zfj5ILLW~A&1~I!1*krThDr>wdN5s8*A2)vX;`lJD8#j<~quru>{q}7$IqbHy*30uw
zPJ(!z^nnBQu9}*XmKKe%v9XZ-H$KcX4h#&;ZVoX|OG~S8o2JHmB-lbLD=T at f1at#O
zM at Kv=Oi5wp(n^=M*`Mg^>zh97p>_5({LBbA1QHAQ__`Zqqf=91z%8zC&i9c~QXcD7
z`T{U9^7AJJV4m^&zyQ6TaM}igGV^+pknnK8AZPU=vx5!SZJnILYisv{Tb~95?5=$J
zV^^3Ss?3f9HY_YH;~zZ0K1g`=%KONoGy9^myL<M}H~CyPA|hpU+FGoK$g#V~yU5i3
zI&QG!<HwI*zIrtb9v9(#VId|sI5=?Q0g*_)#$yP>moHxq1~ZUkxoT>vDSnTKhX+rS
zjEs!d-2_-!_lb##A?pIP1_chvRE9l0;qFB~hG$@6Uwr^%pS^o$B*3CV<(wTR4ov6s
z<cE9 at OhgCvA6FT4(!w1WHXb}+l4e7La%Gq%(1>?aX?DTjOQDgG&1OfxOMX{or!!$5
z_;u`IMh4Aod7N2QQBl!8%_1i!_uwZpDeya#>+d)UJzs;G19Mz2ISwr3U3y1d-BYvi
zR}eNqork$oIiH(T0?#|j%slIMWjM at z&i>J(N3Pp~@TI;&#g85x22wgYKTip3C2TUR
z935~ll4yH-TlM0_%;A%`99OTB6zGv72QV;b?{(>>Ju$Zi;x6Xi&vWsr%F|W(sf->0
zS`MqE(u;pGS52v at sh!>2_Gh$l*T~h|=!x2@<>-LpvWbY$8yXq{Kq{bM|A=zGy!>oH
z=gc)8d9I*!{cG1w>j&!?8>1$d_TRdn9+3^6jABSq(qeU at 8F>eeo3CKv&el2gs;n#?
z;0f3{cIN;S10Hq<Xei*$jJVSrS8G$#6irlF(}St08(<i$f`W0ibYB4VAb5jk!Pbq8
zjuL2TX<1oYcSyNvR~QSNJ4Oww>g(tCd*Q{){QL-rYnTk4BJOnJ>-+wEP<i-`-Q9Y=
zV?5<0FM%DrEz#h2yk=k!U3DiVJ^jSPhY#C{4Uu`k_t at F-f){|rgPSWe9$Mb^_xACL
zi5#o%cv at L`q`A4-{q}_$H at H^U)_{gn!2W$77-%`mHqh7ip}*hv$I at o|_wTAS<}(8g
zrUnKPo131?-qLt+R`tFLcxp?_^I^@@hJP^OSt~ksPp%-ed~VKeUmSB@{?rI}N at xKa
z at y^bU75jGt06Y&kf1X%IG}Fn=ANcpb?Bxi^fSmv^BJ!(Oue!V1Ri}9+yKXI;W41rQ
zyA_*9>a|h};;*R^`$typ6Q76+2h_-;jvZU^#=2lv*E2YzxV9Gv_|}#;2RHXgVPQHz
z4_Y#!%b?~H66jM?Q%6Qd<d=kG5*b&m!C+fjW{=met*ynUrNyMC9s~6bPlh-&Fu)8V
zFG<+4DXG>J6Cb~RRl)lbOG`Rt=7HjZpV-5amv`=$U*9b;!u|VQ#u_3O9G2nLz&oG*
z4RUpJi_gf2yK{%ox1Fy1*^oR9Sof>+Ey at tZOyg%#kM^PP(AK6)Tkh at XxPABT{+e`U
zPC}qENA{R$Ee(OBYo<#_g4kt0eVPJ$w!w=lDk+VQjs2VC&pR<a-K8-gEG!KCD+KJ*
zWJ}a-N*y+IgSpmid)1oHwAx_tNy580IqzlGsn(QVKS}`zPD)9o)RL%aXyinN-2U`)
z>0o3?ED+-a|1mT9Rrof2ea1oWCy9wFOaE{&L_xe!m|owRQpGaqr%#1$EDex!4i0J*
zGwagW at pyn_1;viOy0}OUOvl^XJGgB+?)mQY8nBp%*x07NvYRfWABjc9#O}0Bcf~vh
zA;Tiv`rO3C1Pg|-nd<LNhwR*3MfhIK&_yW-X!7cfv^sDU+W*o8Fh2u$%<{_0hv*|B
zcsHb;Asf&@9c|<2h at Hgf7#YI>`(eA4U_ITYzF;ENox68`zbuZirlO+4nw8h(<t}Se
zt=i&bX{=38ApRN{8jk+`^Zsu*joj{iyk}|H+55dz>a%vk_xg5Mi2)z>hw}Ti_@~}4
zKRTLH`TTh{US4t+7Z=pq@$qz9{;~lXqu{SfO7$OhpHngy9=P-FuR3TBBcr1LlrLVs
z90lzZ6cmK%925J(3?O{TsHWIm{{nCW8V{U$mcV+y45&6$jaCs at Fc9GJ_O3y$`$RXK
z9Dosk&XS~2p*qKL_DqBGeN_%T^R=?#u&}VWfB*izcvT__3JUOH%oobfCj+`eMn#3`
zUrE&4T41nvUewvuHM^z-es6T`nz at ZlL~!uFon1?F^AGM74Q at 8JwwWpXZmzEPA3X{M
zhW+~O+q<nd>c4zZFX~JByaFo}v(?HTcFo at 2{{1fXhP|QT5db5-D_3TBsXZ1~*Vm7g
z{&XSI*3%1tkb%wyihXu=7SyhLTna!D;NEq6dkT0Bc*NKUHV!2Xo<p760(Q=Bnwr#u
z12=4J$Ye1;odz%unalcR%6PiIY!hv51;#%*f+sRBlL2`HtnEyY!qR6TZ*FdDE|26-
zTmm#$Z&U}cswu<#N`OlA?8!-D78Vv*lXFs1aZ%zgOEvizPI7XFYO*$c`t+>4{4fwu
zXD_dCEVPifvA0)axETmhkQBvfVqzEsY-}R~Ol{u at acRJTdI+-EP^`2x(`&UGi0!~+
zg8HjJmdNGSHWmk~u<}Bg{Wg$G4BJyvDvX_!-$XY`i(b7t2ulI~{FG4y0&d-;>*(Cv
zB}Sz$Ny2yevS|qI$TTc0*s&Q2*ccm#!!ya03=9naYkv=vh0 at E*3zqG{&osG<ypOUV
zo&qDpF7rs`VZS;%buevVV8C*&y8v7K<Z7mo`}z5;I!Q-EFaZWLaj&rK)vL>}Js at uY
zsX at MdrsDdq1b(C9*el^q4==B-s+P^;N$^7)kCR at Nmc~~-c7sRA$>HqFxp6-yI~)7(
zz#f#mdSxMb?2b9O;d)H~>|TJW|N0DF at Fyz1TVg5cgw*@O!on(o{>ow}1mJY&E&yA{
zs7VvJwQ^J9YW}^eRW+^u%lU6PK~#1&#|}U)RirW at gi|6C5_TaW+S=M$%i7ClAubMC
zO965>Ha2R>Bz_uK02+&#CO<0O{;p?mGDKk}D>!9krU~$-kri)j`UM6zKR-WlG_=`K
zQ>pmPn?%R^c at Osa`1q`k91jQx at b>lX{Iv-GVS22XjfEaC9_;3YIsKr^@bp^n{q7mO
z<2|LDd;ho09dAt#x9jqW- at h9`?5E{ppa4^FSM2TTqVFv-!P1NiOP`fSozH=5VunfW
z at 5L&GzQM6(e+vji00Cf>-QC at +v}C9w!7edp72?`N&Gq~D{n^Way at 4OgPpj(a=(G<G
zQo%#+y!+hRN(C`3n`h<7KUQZjdT-Os)n>hU!vH(Fyt?`k5zw-U5^skO6-nde+lAO;
zA2OA~krTm}V9LJQV*zZj-Zd(d0HhI%=BK}=De36o2 at z(kSMTlo94~D4tZ?`8!n=2F
z&D6S{=lYr1*&&r~(^&t3?*lYHiF!rQ_WzeKh@}wNv$OEZ^OEZy?g+~)XXfRR?(OZ_
z*x1M`DiZn((l(H-%;sf`{a$(x;jJS-z=<;=ljG4Pb93|C5ZUvn#TTPmZ(wKit?lWI
z$yEy(8JUR4$Pd^U_r!_V&z~=WV8&kbn2Yf17t5NIzfO5xM at I+76F~cpzP<y3f`TC-
zA*|=lF==XQs)tJyYM(98lQ9G2b#--RWr&*eYynuila_W|CO at XThw>#bA>Z)r+RBpM
zU4M5^&pV&SJ3NBAePh7ajyS4P-UBZ4qrzoBJR{l!@KSS2OQj4yLPA24V`QD*zKPv9
zYM7&Q>V&knd`1wqc<SsV1YFzM9iN;$?|VX}<m*?>?7X~_5)wx)Uc9KPso7K?PD8M-
zsi_Hg|3l`3KY?@1Ns~mXaws}F7X2V81n~xNpXnB4YHI54kW^LmJK{*)#b`lbY7sFp
z=iS{!0p>4Xxk3y!h^=A34Z$o%*0xK^%1&-X?Z*UeP#R!5_d#g at 0X)ROaHrAYLt|rT
zt<%QF250>dlI94TP60kXWr!K<Cr at IO?C&=MAlM02s;LB07f&RsRQUWkNEJo1eVp9f
z2#~Ntcq)7SIxBY386Q{jJj%q0l~$9V4`Zf=tn3MJIXsjuE*8Xe{$!DxAHRJ|Jj5qM
z8mW9|XU8``KOf~4zJ<TP^w{_~+aYocJ($1t;SO<<$W7|Lf-USB#VRRz6g?@1S$r9^
z5RfS(35~+Kd77b^eV-Y7N<#yixj)UQDkvzJSzHW at iJ`(-2Y5R$Je<LILiSl!7CZ6-
zamOE+r7lB at aJYVVepz}QLY~Xty#Nj_E^HQurR4qn{ZDgph=E{WN7=HQZCo)icU5^G
zvCGMsEgGExzye}2;#vuClIwL1Oz9&C)=#ZOWh?TarLwap3aGaKy at HZ0_$?<TCG|>m
z4=ZRh3k&Z7v9WA^z^NXt6n&ZSw!C9zn%%9#WJLQ;ERolZI{zo=_5sXc8(gcW-5C%}
zZX at yor@*WKOP9#^VR8uOL{@tX1Km0j>uR&mS4O(@{_oz>qVk6uWo!w}%Ro`Eplo+A
zE54~o1^dF3mX=od{laDd9bH}f&z?P7QB~D)SQ2P?yhkBjKFF2i<Yd=IWdOpNg#}#K
zT=itdVhtZ(-`H72PYY)d8`v%YGd;oX>6n-zz}v at j9-FdH^WR}ogir_OoJ;=>Xf!h8
zq9OtKu?(Y;FXJn97NuAM>JRESXwG9te>D<x8kRhM`^IJesXlmuK3oMvJf2r?%w53_
zw_x_yOAVYoJaX1f;yKIh+XT4HJv}|Ht;_JB_nWP-pHH>Vnvm0wG;jikA4TR2hYycp
z72RxCUS?L-9e6y;!FL`I$jl{QPwRjZ!Tecl;iAg%5$m9_<{>dLQ9WF2uBW^EdRw~8
z*2*UqydOn$+nDPiqozLL_A3B_85spdeW~Rq=kX>gcsa|3{_^jyZM*UZ&#u`5)wx<^
z92pTo1{Q?<3uo_>-PsZZ-Fy1<>FQrYep|io_ZqR%Yz&hF1Z%h0zuywe)|nOgbAmej
zZYTxgSg%<S`T%wU1sV(Vs=nj&Ki&VA3otN{;z;J^?tW}+=7LzE3lKp{Z1a+z9}EK6
z)MxVW3#0OT-DA>s3k!MQeG?TBpoTq!SkvCq6AA=ICaHCB at HlXW>^Bx6 at t1OV`1y0(
zu6KuN0plHUKIiQ0j6EoCd`Tj}k~adnl`r{zt=QNGv;dGz>;~-YEX2TXgjo`~N>&f`
zc+UeR3D|3HZN=-|L;;g~u+;TkT?w<apJd`yLx5u|U$}r-tazsp)d-U7Vap1ztxmpw
zfIcx)=59|+SYb+r^a&b>wC;btug`W}td|z%kd%~k#xC5-$|L>d_E=z>o67~yb{o6`
zn*sLpc>I{MCb=umiv1+${+DMAgr4^i{M%vdNDmJSoO|}<ZWNPkXHiiRjxnM=emq9X
zYw>ArE(v at CL<>YE83)T3oEA+7lhwmMepJlP&c;?g*xVp6P?k$GRrNi=*C%HYO(z;9
zFw3~%(<iLKprfPvr=<n$8^_H^xJ}_*6Bid3gcku-j)T6wz2)}3w9L#D`;sKwxUih}
z{(ag9Gk=R~>xn=V;)RX%=R<fhC35pCc0^A8BKk#AN@{jC4?qs1FK8tIRqVb`L`)o?
z*0uvo8M83|@SHkz8@|`ndz2U<;`75(5H&cWCMgpx<)ChdxN0&p(B8fen0WQ_M+zK2
zf1 at p3gW>RA2*5x?V<TpTL`FucYG{PYvj&5~{CA2(B`z)w^!Xz&SM{ainDrh1`0<6M
z`IOYuXowkLkxCjGSYn0OnINwK7B33_$(Wd$!qf@>l}1MOaK&h6TU%SaG88a*7gAj<
z&2;oA;B<Xm-9FfshOF~ZNz^XtbmXBJ#9 at h?L3`@y>(_U5(6O<xVb3{u7Wk3&R#<fi
z<868q4S<OA^U-e8Upqqo0KilP{C;E1Mjl>&8|)e_*2&2UZ@&8bpS)0ru>erUszZqG
znAr=$9%zv~C at 2V>I2fCh;(^CLLhs#F3BOG~OyUGJf}Kfw3a=QH+x6u4mBzC+Wx<+&
zD0uer<pNFIq-!M(2}qamD{-IgB#2|%Zs>@cp#S?WEQ|yb+4PHyKfJv57GR77SUA=T
z{BJnLkv*v|A&P7=LUW%VorY%$!352AF&-W=Fh4MqiF;8|uxMr at 4UzOEuU|KOdwH$Y
zqVb-vSK3ftUnIte-<u2IsEzi;HDL||Z{i1M|K3SWJ%$}(^0YF-Vq>Xs^bsOlXZA$^
zFg`mwJ1lp{M17lEw;Hf9%m-4I=;W!FHGaP=K+)n<C&K;v at ggpQ`_|yQ!|*vOE-u2S
zu5(3j<I7-p0ili=Zr83wL4d|;KFq{65KEyFcpL at PXkQ%Z at fNEySPT{PdtkW4du7bU
z&0$R+7!B5)<2f|%>C;#ej?59em-Zc+cn?$o)jL`rw&=08#vItVIGX<s&JX-|^C5Da
zcjXuep!@%&_P@(5EGwf2&{|&}V>I=?-EDgC4}Qx(@h9<HWY%v at etwiB7qpkuci4~T
z5CH$~_8N|~SY2JkcE$Mp-V5cuKto&Hy~hd9`mcXYlZuFrRtD(G);aa##}8Us+Ta4+
zypI5{slzH2z$XDmuz@%>Oa#gVth~CW2D`DovrU<|P~W`*bg9C9_Tb0*dKY+~_07$X
z;Q0y)3Wz5_5NFGn6c-j2aHtFz1)fO&tbh}+!?r`b_8tvoR2A6MVtT{1KuU9h6`<s?
zfQce#ZXo|7KYyxesb@(0obefrc7^DSID`c}&z@=P=n!HbQc}{B92`Uh1O(Xx;RK)=
zF%Sp-+0X}_{b6u0%`qDuh^x4Y?rBZUaV!%?6r!WM4I7F5p|N}r4^4nX=PDh$iWyE`
zi^nuGWMe>KVWQpM?sk&6(+PkJDt><cTqTYjJT-wAasIR8f%Xv-6N9Z+ at gDwf(B at Yu
zzI#6dez!3sni46{ejq$5>M45kyn at Cd?aR+f8AY4&wJi_&t;RIj+^Jz!i&jyN;lD at h
zr!RWojm^AGLs+mt&En7!%bus%XTobYJa2eSyP25kpD9b7PEVdrT}vH$TYNtKM|^Vf
z#oxb{@&M^|jg60;Iz$ft=?HE*^uI2+yt#TTN9Q7VV5a4#*p7~87iPyAqd4MKdHDFE
zch+Z>AoA05$tRArrii<5&KpXC)uc&!+{0FX4=&}*b;Vz)I`*yD%xor_E}u43QQ`02
z?_yu>rk2NoUGOUxGcR9$rV_)$!J%ApGQGs|Mk6JQZ0duAgxU5?C1W+Z5w>E}nyjQG
zrv3_-lYiFQ<POo&%GdaA;2_HbQfx=*_N_xu?_deqRRH#u1F<Kcl!TP(s1<|5hxK)L
zS1+$TFpjjpZ<NBRIUcvnLa?*Cc{5MeM1qGW0vqSudTL1^?YF5OU^j|oJrIknKxqI_
z&Yc(fsC>9(9H{DR&jlm=esC#mcpRL>eCXIQzHT!@Lg|sdvYV0`x!M|biK-m#KwIqE
zpWYTqw;>=KkDN?MPuIl9!g28{<DZ&=mrHsrzK|_9OnT8Im at XS|DV&BS7s9xt?>f)q
z^fVCdG at H6$g6^`LTEKS4jM$$6^y}#99pX1A3HJB*pBZp^pL2UZ>63sUCv9yyhrxG`
zfPSli#1ZwF(|dBpn!K;S-<3b+7Y8S2#|y(Uo331q?xza-aY`%|lTe*S%hsJcg#B+E
znE8z>?&DB_d(qL^@CqsG1wOdPzlMV{Fp|NOOd7=o2M3d+OZzB-iRYqm0CTHMJjr_f
z%mh3uI8;t{_GOUPCbj;_dt2i)IK9@;&`=u~9XPxuE?Ze#c(QVVUT1Rxw=Fyimfyd6
z`!-M5vZ?)hsbw>88G9cUPC>!Aujw++9d(B)-NJG9EkN;=xX|)jeGgu|cu|qaTgOgJ
zOgu+C0J8ZR1iAah0(n4G=K9M|gFe#+4CjACWj)LD=#r%K$Uc6PcMmnwBy-X)v=^Jn
z6`Iya_O7bOhJ+CHzqX~vC4;54nQ6~l at 6k8wkXov)FBdI4dHQq|HH)k|uy^<65e471
zDXOzJ?ZIHp%Eu+$pN@=NH~*Ru3GmJ=8L;!(H+6bA=r5<fzW#<nsL4Bbx~VlXy-SXc
z0t<sxkKf$tQ at jv&f|Hb#l$?qx2ly)darpBA>Pk>NjZICRuzxdvq7(D;>VJO!$^&iI
z1=_r=TzaT}j(q{2^OZFPm`ogIO5oDtN6^sZ;X{P0h`<_1y>=Rw?SJdU0xNH5;!J()
z-2IOqKZZ~fF<jKp$l3VWs|$K{ur?qC=#9-t-EGn9b^BI-{hBsPmnX(W3fK{CY-nf)
z3_c|#l_I;ltaMb^f(W*)<(8D^+UlwetmrzJS_fE0GbmW!UyB<1NhqIgE)1kBxTP?Y
zhu-;`Dxoy+#^ITa-{xgsU&(T(;dIcn1!nI9Z`4F-nwzuX3jnIlf|OBK?yhvZIoFj(
z6B-_#5EVu7iJX{HLEy3Oty_G+ at S|c+$XsHNk5E)nvi;{i+o2OD9OH#!6f>W~PPgM?
zZLN&spz8;!9P2}VOWLZcWT7D;mqCk!n8Xn?v2k;U3z$}`;`m~S=+C`+Zhd=k#AE(@
z{5!8Df%&HV+mkJib-{;Zg7#AFPZ3Iwf#ton>*1AjpP`kIl<cg#z5mpiGY=qWS0-}F
zbCO~Y`cr^zO>rz*0A*uFPlkXGB<yT1sOc2wnP~23-KVFgr~Jr6P0VR175(qHl;<HX
zR<g^W8js8P>eY~OadL*j+W(7@@M|xP%Eyed_NhVq)P{F{X;>B(%Oa;)TU#q?*Uhst
zk)RbqZ%0Jr-BcU!J72%p^wGDKgOMQ+J`Xc6JkTlNU}wK}?V78z^HYe}=F%M>OV(GE
z0hC=_Twa^klK{s#8Lz4g4w?sGrE6f2)R(4ts|YW`GnK-zMK_u>p*)BgQP&C8xz3!Z
zW+wq`)pGQ#-JzV^+)hM_k0A#b^u;h8;gxmE3>ajdT~A at h&c at FIfKKC<G0yGnZ5%d?
zqqA{lcHr*Hg}LGP?@J0g|7Pm(&H{4efs1Ry8fc_TCj)CVcOB0M33-T#Nfqdt`%F9A
z=x=he<s_haI-Z_luxQb+9fVw;K=93kvjk!n$PT}ynhObB8i?<P#(ZB$3JMBd`)=47
zmD?T8$;<1y&!#kkvmdv2b^zwiPPj^dCJ)Dgc3^l96B3ke- at bk5=+R5K7$BvR{0Fq_
zgdSev{Iflsflnamza_-U!GY78J?6S2eb;9cSOV7Lo$X!`Kh@*41qX*cI{#~N at L5TT
z(9X`!;!s(#uz#AHGiPROj9ytC7<?a;0j_4VIp5d)!ce3rCs`B_ at 3noeK!2qhcbc@%
z1NbsxQc{FU;Mu8w*%gUAb?i6~J?L*>f0dVDnuJ?egN1Ma at W$>cFDG;hiF4<i`L%;D
zU|THExEK94=QDwYl2cGTV^a#d43DEx6x4kXXke&s43;MpI1auOz4cvaXTGd0ANI+(
zWt=lwuu~x5s4hg)nejL|1Pz=NosdhU9swp*pmkUVf=p#)Wye#6yEe-sAG)i3+=?cX
zser>gMGsL|r|cUT=z_Q#>UHZQ-ev(Z&AuHDe0G<V=E2*y;sFrT*JtxGp0q9s#j(ic
zp0)qh9!AB?BO;RUFF=q|X9F~){AoDtmRC}8KlYe}qOh<qLcXh~XKwt-3!Q)kt%XW|
zfJD3w{Nsxu6(h)7$H9ESjqVyhuQDl4r4fEmv;QtuZGco`V`F at HIB5)%@Ew)dW44=l
zTayzL?eJOMQdVHOI<R2{YL5jD9XyCJ<FIEPy%0HUi2L5I52D<03D=M!lPVQ3*c?DR
zU1en=kJ--i-&unrAmFG0TU_7Wb^|UuW)!<GoaYcZ?qX|(Tt<;;%_SE9WrCQaXYaAI
zvxj`uNPm!#p#|XqqZ*DUAtoW|e0WMX-)m`z>n&Sj@%z0?;ExlFi<<D?#K?%er>7_W
zvAMbFKK=Fhy|}nMP)fXfeA%80{n`cwOdB&DO5d$CpDKdIfJx?S8P&LqlR)GQC!u7L
zN4WJG4tQ)INl3Z8vte&k<;f2)a?iBdTOl(uQ}o6cCNK|ruIC5HmLG!ObyWMTMMS9b
zK6+rlmkX3<Bg=u2k+D!Wj~e4xp?<MuTZSAyf$!FGrcgSMymI2%bRJ+tK%a6z-{=Bg
z_gMUqOhR))9Snu_!i5V(lLwgOZ`|O*paw{%&aIis8Wa?CROALF6^rbT2{CtmUf!%^
zk()?RHWmj{68W#)Soxz4rvD7sP8WcgO;hv{yahFn*A(8{UDG-$>wk_Ds0rv=+uf~|
zkGb}f*v{hg)fZ2f-v3Rv`}X1 at v$$h6c&;<Qntf9Y^96JT``#inf?ii5A|l=qkS5*b
z_WJKUelp;Ud3y<fkPkp5ajv at i*0MQz4FXY>=R!rIbt1wbusU1d-MH5~t1W`~lU%)?
zym3E%7$ac-%ZZ^rE~yNd3wxapj1A)zaK~u??Kp7VOr&Vu%d88-?=xVxJbr%Ds42~S
z`I5i)ATiU_@@T!N_2)zH0)C5~S5X0-m=KbXZm8&?vmGb5r;6j^z+>aGva-I|If at MI
z`xoZm)ogo<O!N#4R7Xcg|HXm+^5-Q5A)ya|Ga|72{c`MoA0&r-DnmBFW@~x0yU0Y!
zJVB1){FNzz|0vFRjTEsQbgW>4{r~jzJhJy8G+J$w``eB$Jl+)ZFeOC|oHw_)cIQ&W
zl}r1>&VeHL-Iyajefo68yLT2pDqOYz>60&+)<xrxU~Jq%OS_L<5q@{2MeseVoLq*z
zEmsRLV{9Q#ONU)s;qBRgHP{;xQa(sZiU7kuDj*<$GrJ*9<H)Zf>)BR#V;nl9%5kte
zUwc0e$^f3&`-;e544`IfG at JzwLn4*$;502*3*(kE5oI^qvS6oaqZ;w#1N(ff&*~Og
zA7DzxM(I;ul9x9(!wmRT04|)ty^E}cFPWO2z@~Lru-D)J@$+YLbXlC=0wN<07K3nK
zFiwN*du2V-T|f`<6es(FR>OAMPoD<c`=rLl#}kwqL_>&k;XfaI5;hlGn*r^UXODjx
z^mh-Z4!~o(Pk?!<4A%y*#jE1T1Jw1Gavs<llHeE)?0V|x=m<|vcHqDVFwFajiB09A
z1oTs|4_i|yPM51Z9Xx`@?;SpUKR*5g{EG~xNU6k~pu!Pc1v<pQmhr3)zKMwFH4bKA
zWygUQ_2D$1epI at xz#g4sV?!~zFJwiTU_rQdrD3`)!xHwd<n3E5*8#2)SLL<j%C8pu
z??eUgKP;aD at ZmaJ`}oO|djL9^g~cp(Ys83p2|F`kL!jCKzf75^w|_3<1Rg!!Mp71k
zTroHPW-C>7b>&a7$JriT62AHMSi;P&$hf$01HLAR-W7 at LtgKIoFXbHE+uPe(tXjgq
zsPj8>=8TZ<x=lo*1<qauDKlpDpz(f2MqDTb<Hy!kDgfceT#b~9L at U|@2M#a+<4jFW
zy>4wC3c-wxi;D!fg0ULCkoFl75p2Z*0ocIM at IznUk&`OXcpV1ptZrXqWF#3C6){~V
z#5A0`U72Vl43zeNqh3nhylE02_P1R9({?K%KK^!N6k~l`8;x=Z{3Ht)SE#YTw9%7&
zZK`T&^{^YXOiX0#IA#^+oi!*igeYd#eS3KgM4rsXvETIEnt(!e@~_IuN`Zn at uYHxG
zjZ`M1p%LHqP*AsMXLkryyp3Z^!3$x(?0c!DJm=-XCUtj+zNAJm3R-~E-fy<TM&V!x
zdFlBlFH3;a>uUMVeJ?!;4Df3?2SH-<=g&Bi1RJ=4WWe^2PD9nBMv32W at J7Jz%}}s;
zYzVygBNA{O9U>Am9G+0MJ~?|Ab_DpVhU01<NUrV_3p~CM6Jt1k{=9L^!I7k_EOs1Z
z11x(#T_#u==9Klmq8x)S#F3xICMICfc!UBHJE72xh%W)V3%4ZlW1D7W_S*ovwY#fo
zQ42)**WL<%6V{hg5>Q9P#a;OM?Ij+uS~QShB9{F{LXy>y5BqVxiA5>X%WH2{{QaeS
zAGj-KULFDLfISOU<-mTX3<AbHx7<1E&Vl)?tgJ9WH2E(NG(@m=ix8;JxY3hf1{|h?
zV<%KO6yc9j*U3v(*48fkYVz{(q at Po&Iik7AneItMbLVDf<M_ss^77EQI2zzir_ at zm
zv4SO*STvF&^x(Ktki|N>x|NAn1c0{SP{+A%FHfse76qq>JAE7;X2H3u3EWy0iS<VS
zZgGyti<d8P6l8*d)faKV1b`HRWc4rzXshpz>s`Ja43PB<G at h{<9W8D9ZG=DwK+{HH
z!TX9OfExfKBc`FDX=rF56Q;TVk(q>^OFbC&5%`B`t$%Yl$4d|x4ZxOq+`^BFJ4R;&
z?Irg*$PsjbZYO?z^`#=PxNbKluf-pyzL<+Dz$0L*Jc!9Stm$T3`XqQ3 at R2xdI{e`S
z0nT-!mT#8_jKwp2-YbQ59UXKyV+vGQWg_QwcDzi*XSjQJKkWI+!rNi;@ZZT2;OqLu
zjM!H(P0Fhxk)uwCJd_Yfj?GYYrEhGZ^HvH$)azgKeRn}^gI}-D_t7&77+=?U92~kb
z-zS12)Pe2bc%k{eviO9A`f|RZRbVSP-II^u+4(4jb3i(K9}q>WYid$s^FwiQag01*
zkkyOF5?dyjK!N};W6!Ahp_-IlU*hlQ3-l}id4Lnj!oM`d9&h=5z~k at kTAZ(oC;UD3
zNh#tyu>26VjGco+dEqof1b~`vhZq>xB_)k&+)QALA(Sn{6BgwhAe%WqOaB&|ZPL?Q
zYBM*i&-G9MyBdMrqQimSV4Z9SBWVvFWT!lq%%)#ttN;R2nVk-(ZPV{_MwyqdgFB%0
z!{14IEk*;+mCwBF%&$>Lh$98@!bVO`4pt{5JbWDV68Ig at d2MYv3k!?xgh=d`{r$T^
zW-2YM&`AVuF5$+N25?Kfn23#y#VlNGkDZdzQqD&Zi at D(sIMqfL7pb|qx%rG<M|dUV
zv&i^{%=J8f2D|OOF?YDv)w`|bu^=6QmzkLvSd0t*rx_fm;IlTBfA!_juNiV}<;N1H
zA#&ghB)|?Q at m7;k3*uwPkMnm96BDT?1KcZe96XS6gPhC;Ugr8-cL6}#*37fGkVbat
zQJ`nobs!~$8A$xc@>FuP*1{{#N5;qZL(q<s-BCRkYEox|GiCq)LjWj>iv<Jswr at r~
zp|||}&!j63zSJo=1bUoNOmq^jLddA8!yY|4jIA!qkC`q{wKA)xo}&{J6T=n2AO~gx
z;FiQ=Yis*2K^Pq+U}k2<Udz=B`{~=`K;IYAuz}&6b{wj1M&#7yef0gE$7#FXA}Y|w
z at X(*K#<kNJZHM1^P=Gy$sd9V-Via<miin9*M&`KN)E7+a^A8BX2{J{TE3vSZc*$#O
zYP$93*KlZP5yMx21R>i_4%HkTcVNSht6o3%e*@z4d$muQQ1ey!7>E$y{FrCP!oqS)
z(mlfdqBU;X`fL|lLUa2oYc(7q1#rlPE&V#O&Qttjb;f_KRlGW#?+JKpxqWXIFt_`c
za&QKufNAwVu<ZmYmEJ4tK)opo^vJ=OUR--y^n-rrOC<2vO!&WX3$4&Wa6EZX03)-r
zk+9~@{1<|)J(q^4w6wIkK{LF~vtyC<XTp%6%>Dr=X{gCV;&V!{x4>G+C at DiAP?VV0
zhYVixkq-=%#qKFvzke(7KC)<ks(?8b|8fr|WhSjd*XebSXaQ((Ve4swYu7A6?0#(S
zBea9SjK}owLa!YMpNOXfe4cd0DSyW$-D!8PVA2*gz)^`gsvz*#jsKGk*um>teFuRH
zPERtG7px8k{T)s78u|bl|C3bE=YRgektBGIP?SSPMmD1gP6ObCu@?Vg`QH5eEp%3Z
za4ci0NIZG at -isG}l*c6xUA=nM;C-M>uj^+}?O49s{qh>)nPf_YJOJhjfV at l&E-oCq
z0|6L5oOX(0Iklldx%&5F752H#QG1M^M-CAjKuUo<{`&e%2SZi6IxN_qoedm#QF!$w
z-nUYu#E}cB0D2_~niXvpQ!$H?{Z#uB48U67({p%Zu7_TX|CRje+8Pdv0vehHGzW7V
zdLNuq_<6pcn3xzxAYn`Nv-VtkItQ;m!a>Jp&KzWq|99cIG{+oH?1GM$S7ws-E-9gi
zmp_0Pdw{s-BqUaDoY4VB!G7|jf-=sW_&1Jm)ajbm_=euzPkJX}xtTj-`G$TSj&=a1
z^Y!ajgHnso_I-!ikHEWOd;l647#O(qv-o{Qq8?$6P9*FHCewcV_Kiu#SM(q~JwyoB
zI$jzUSsY1t-N7LSr at 7UKQQfGSMWHe}GExUo|73|x2Rrx$z#K)Mu_0}9b2E|zV2yxZ
zm5I-$fOF3Ls1%7H1MjFdyag1ozorMmD~4cUOA>N&a?oQg`6R{x{{E~9&C8gS^x-Zk
z;3X~Xby{<2?Yg98A8D7Bu|}YAQ$2*n1bf>4Y;wH|lJF)pvRidy3`a#m?QZ`LX9=|V
z_9uoHJ`wx9f+ii+yUKvG0D;OWDk^%PJ>viEh2iq*D%O4X>VRRdU;pPm8;&6BJwrs~
zeb-pv6Br>W#As~D2NLq|@_5|j#INoD%LR}Fcr*vPW2~m__VXJ**t=1UM5a)$N-%be
zWx<gQKuuD_9F{>Cxbk0oiXCIooN<aXqo66c=Tkz^ltd7-0SkNBsR>Vg1)Bkx?gncJ
z(5HQF)*$#S4uODm13zRJ61seD_k(hCTigFa((?9n21P(P!&kie1R(j|*T*I)`NUpA
z<-+sx{aP8a=PzG|_diy0qxGnH{Da^Qq-C&eql=5NCojZ0^M{LoOD+SJer#+c4%%Ho
z1?`Q;)U{h<6cDiZOskW0`A?lXMMh43=lS!~n at huO1q=jT<^UPa9v=Adot>TMdJY~x
zP6bfx{_`6ZX!~)!sNmT(&}Jc_p*Y}GSXkJ&g<$y&9qLnjw<IBjBxTO)F46%zhd at Yz
zg`EGrxzPIN?$Yk|8Y}xK2eZh{rsyM>rT6Am at _BVa2A)Wrf(8hCtnsSL;OVaXr0}86
zo}MZ=_7KDUBpI0vhnyS>Cei_fLog3bFo^+{K>~3e=lQ_en12j805&jYlnAa<Pc|M&
zX#62>r2#l|^a&a4IJ`F1iuuTSzC>GE at F)Bxe4mi#yunxh8?&7`IA4|a*%5giorCE5
zpi|xWLp?ByQ&3RQ)FIZ~Q95v!DLp+MlRJBL_U!{XhBG^{!`r`th`TT*Ak_0oaNU6H
zSnP2rV3uYE#`f&DhHH<*w?Ee}p3)14_k>7pe)DEH=nT+ww{>L*2`5*2&4MyPSA&SA
zx9~oJB?LIumXD7T_^5FUUHcvELp(Tm{4NRAtEBC7*q$Ahq5qA;>07eA*G2W1N3Z|_
z$PxbV?e%qc_nCH-q^WUj2bRM at E~ux7Q32pkFbYf<#cCJ0$8XMd<)NVf%XzZU;32 at u
zPHP)*zw&%ST3U>GeV76t!#JqUX`|Q<AD|q-Q7Iwb;RHdIx5WUt_Pqi)v%0H`9-tW8
z!-L)7<hzDXpB``3RBpYQlhFGM45PZbx;;;eWf#J$am(}jfQ^8)BY;Iy4SN98Bh%C2
zpqIynYW!464R`#mY{pRys9Os7T-0A2tjgQW2&Z9j&BEc}Ku-Wf!i3~)vzCSb@*E=f
zxN8YUlse19%li>RKL%g$5j;T^;Scsn91fmr(rOfbbaMw at 1E~`5cf98sY?yuhT9lSf
zGxePLWl8_7^f0 at TdXbV3jAs5w@qsoRZA_O)95|dK<<Jj52c(aI&){hpwdKy>=E4Bx
zeD&y?9ceVRb#-+WvT0|HP-aIWQ(0M=I<t at Wmu{`aymTW&ZuZ9BpWisy2HRuXc5xL=
zCh-Ha!BiEjs0X<NdJX$*+Z(3Vh3}f%3im`G5ov$t0VL?*D0~z4U2HGBavDCrX2{ms
z1K5m(h8Ac8QAg_f at D*j<#a_X$u!KT(-8?`5NFn+G(As=2VdGLy3YPo3n{YTKH}U=1
z58*2f5J*G9!f at 7nIQ8){<-1&8Mt>U-l2RW-Jpk)}W4+5~arixrl$6x74Z$Rlfj16!
zYieo?1ve!;=b5r~3O)qH9 at egwrJ<tYcaswpeOS7ACZpw_ at lSE}pFfjxYo$*Zk;5wO
z13KEIxheLzLZizKk2P at OJnY)JzJ~jPvJPJtN<%`3{I at 3`<5)WExZvPG1>g<utuozu
zI>|21wjKhmf^vo5ucE$*5BG#_vUE2*Rk+Lh<!{(lti+jH--Rlws_;hk#X#r7VIZVo
z5fNmm5^iB0zo*czK+8Ih)DdXTzX!=hM at I)N3y&w5qN3I_uf8;5WjC(zE&oyj;Whsc
zKL<zlmp`ZVi-IFAKflUfb4x_xs@>v>bT7yV?4yjOV{s>B?x&}NN{`|a5ut~$s0gap
zP|jSh%lmdz6ou^8$o^xp{z+E`CT)eS9e-31+`M^n=vGz}%I_p0b3!m4RL_SF9dhPx
zo)97q(o$2aANg>1bZY8qmW<AjhougP$w+$cI}teI&wQ`;0G{=VI`khpYya)gw<qK_
zRv>=?DUn0;>O`xA=1eyya3g-;NKz4zE`ulQ9}n at Ck0m`Zl`ZjHFamOhV?XUrsXWWi
zCp#|XiNpCMBu3AiJo)U)zMIwwA+3#XPy3plK7D$~>j5|!BT#Tm{Ee{_my%+}cZA)K
z at jduTETOOO$@i~`Z7FJ|oj8yWPo(zt_Eaxk6fRr<R*K+1 at YYGZ&_rys^Kr$i*6H0c
zkDpin$9GlU@;`3r$WdcNXa;z#Hi)*1oqT)hla~72NQ;QAlDRn>gP<uTFn0`)^CY?8
z7gR&qsWih~m#O@{NpkzDeMD~Ex)m+CGJLGXL1Pxv4imIw3Fs7fAC0{o4(w73Fa5Jv
zcY8ljyokt1;U^j+4$*Xn4&mhSBLSE7>6_NoxP(2^Ehuz5$*YggqyPzNun_*2a84#2
z7Bs7Ez-}5dGh`r2F`>u%&(7K2SLZ@$w(B2Q6ymHM;7H$JSyS}te6CYIVrH0 at X-mkZ
z>#OHtvm#7U8_n(7SKOwzhwM2OVDkihlJ0h0k9U at 0raTD!LjB at c``)4>J)YLfv%iI6
zDrE*9oz_SC9)I$}(H_V5fq!V}=*;y}rqn^e;<Wf8>ykV0y8!ao_44XM{^}b&-q1&n
zu0*;_cX#V;cyN8*SNr&+ZmzllZfaVRk%0k9zzwc2_3P9w*H&Kh?(~-1>jQTWqw{-b
zGj>qTF-}5C>Zf}lFa3hB!zmt~V0Er~U at VSeJ|)s$21(W2cDGzM6z{EeiZpnAi2<Ps
zyEO`YPNh^|I}mFSy_cVAyC3Sc_>3KWz&pc at 1;h&n^@~kWmmrhgUYm}wyGCChPD4tE
zBN#c07#SEoe)vG3?8Io@|NT3wpkQ%>6RPBYXPmFUdKtxXW%HU^#PoJurQ0+}%DZ)S
z^3}c at _v|GYS{|DPMv_n+Wy;wt%wMF7RK_~rpGPm*3wr=yBS1q0bLI~{{O}SdL at p9w
zS9ofm%bz<ha?9qXWGi at u&8 at hHUTGAb^WO>l{QM|cWPey_mWZE+- at u9T@Zn?&>bJ at J
z-a4^Zok?yymagqM>ofiJ_&F&l%P&d7T-p^bCP!xO70gajl$Mr)X^{w?5jxDJ at r20B
z%S+m4HH7{*APXxy;v44M;b0ulbpWxu5NYsWwg0uP?wHe;#<yy6SWkwTFrX!mD2D*5
zyX9+2VKUJ(G4e;3IDLODA^@7IzdM^K_{#c|@#=%xfBw@{z at b&5lmh?$@dAHpXljC}
zR66pnm;Kr;^I9?mqV>$v8wA*EyB_k|+S=5Nbdw{wrhzH0yA4eZ9J=gmYy`5hvN#ac
zkiXk;;0-^Lz9MU;=u<m~QgFN;H8nMM(C*byul*sA24q3NcXR##`<=A3G=&7qisgaB
zr?28MlGoDE*?TpZr$7Il4&<xY=Sceg>xGk{1#<#11+t%VHBx{_@#VI~YnWDh<7k+=
zZ{HYzCmEDnCs+~>F6U4`8#h-Eap1a>6HdM;%6XWnaF+x_Au9PT at dF1A5YUYR2eQ`C
zx_XrXA_ETK-nV;HYvPqzMuAgbspSP)I-}P%w{`P0v5aMVdmD4C0n$Sg`LCUMn&`82
zwbofUo$2slVu0Wu6)yEQV&$Pu!{Hadtn>lg6WQC_zc8-s<k`e97pZ&);$s)jCg1I?
zEpJ%Ke@`$^MbjTY9s at xI#Ma#_Qiato`)NHu3(olg=<n|s2rW)5$;T03MMXt;>~Qla
zniwf5sS4xK6r~@zr(R}dv16eru-vVkpT*{umiyTiA>c+coL>8o*IFa<?Ul6}PS?Q<
zK+xbIr*YO3N at V2_&xQWd+&p(bb$y%>2jnb8;{+d#+piB}6O)sfj)U)*&RD62&K{YQ
zno_)RgUjyQi%6ffsUxkqxx1cVy&KhpmT%U!FJEA~1SW?fq}1}$(bgNFe~ilQLcv$Y
zz)@AouF<84JCV>m>bvz5yquLCbu&m>BD&G}zB10ht{OitTDq)f&o=%aH&p~iJl9(s
z2g_X=c&|*}@ZiCNcx(@nley7!)<08^cV(iPp7Pi^TICQ>|3}V;bMfbX$-bD-USh6P
zCT+e`wN!gIA%R{{j9u6|!(2(K%l0aGk8%ir#^aeho+U|rY{m`-iSt`QuYETEnkoVO
zzv(V14RGu0gfEQB$vr(iF}Jc;$KZlxq=}wh2w=+73vnl^d92Dmbn{y2cC~PeIsPDn
zt^QFPkYa!8h3jl5C+u#jw9f<j-!3jLtiDw(&jptHjs2^tr at gmVb9`Ubo<af8{an9|
zC+1^P5)+`n_*82O at Cy>RYkU at wm}=hFcLYMB<=|C2JKi5ZexMBg=5T~rARzMeK<@n@
zBt8&8vU77^IjR-tW%>PD%-7AMe(>Od!P}cWxyFGhzPk-m;F6;t^zu!r-drpXJ&gwd
z=H}+cEkaDX8>ilo9XNoq)+-VT>Rz`jbk2fb6OoeMjlfBdQH+9T65j~V_B>~l^q8|c
zbge5-E4->o%89{df9^HYq{79{q*b>UrZq=_ic6+>axDM7c=6%~2yNKW%E`$IO%6L2
zwH)LLJvFUuf;Dn!X~)6a3swR_VbLBw916j%?Vrz|KTe4I>~0AIZ<(K;UpGCM^D-SM
zLOq~4h at I;H^G92IdkXfk(4aX&mKH|KSZ*#@Bpj9eYteczQFeLdw at _%M>;T}xbxX^;
z4GqfQ95j-I2ow|)UJR6q0y^FW#^cI=*990GNcpYX9^h;>F5 at b+bjYYSYBbX%Z%32>
zX^t}N#sjDBVzUg9-<g at 2OtSvxp4mPDXVWl(5EKeF6+wnAej9Q#pL}o98L+GyXw*{1
z at nu&x^7}18JxLBkL`T1HPN2|jx%;F<ltqs}-)Cj)e9r8VF{!DKFAU2tJry+Kvp&A1
zs_g7+CcPU%1&!URs;a>Te3+IQZQ<0Nuf4psHr{ifp-}P at NNMs=#ee`%?yT%Mu?+7~
zBJ!1YP4)D$V>Qxc{Bj>VnC#}i8aBEl-Np!JaQT at E6>!`z`%?@!idz^6`?J2&f at Xjf
zw<Exj8Y>xeIRMp$hK8`FxL1d1@?$7P$*Wg5ysjdVu<mt07}bJ$Ucsf}^(0fD-byRb
z=kU*pc*CMYbP0PlK#&-UjXbcN0Kl4Q-I7?%d-J=zWn_&-Be}PakAlEs=4)C<$SEjF
zab^|?3C3rgf<vaJrZa<8;viN at M*ixPKiMm|{3i&|1>3_{ynCm}9-qIIA>~C4ZW#9V
zt$4Q1DZD%w4QCnqC;lYoy0q`QYRz(%_xNUixxF1TUTvA_^Be-lC7~Pbcy_^h-O9 at 9
z`quKOv#aYJP#R!yhGSm5IJxlHF-ibxc<Qa~ZJZQlU8`-~W;pbRo?A0G0yt8E3uAM2
zwY0aN-`xo3-K~{NwMTzbo{(k&LOWqZ@|=j680CIy-_r1Vxd<}3y{%`OL3yjCu#vk<
zL4TDt=6cKlp at M^hUA2}*o&`N-?B?R;e#~@##BT`8DdOU2esa%GPEBP2Ua1W9m@$9;
z9d`Jvf#_h at x24yo+Sb<A=)fx=>ev0(i_NAp`@mFNhS?3a8ZQ3+?#P|~4$iit2^b=K
zdl?}fz34vzb^z<}$!8>QL7e3EX(^A$cu{Tb^!zW=<)ts9ijO`Ba`!22QY_HJMCI|O
z7|X$LPtMwp%+G&$-l%)n{PUzZ%Q&|~&~uISo;R)8OCl?wRbESoKJRx|fr2uP=4Avb
zF7%h59qM}H|AHs3dD0b!n&SnGo15;E^xwIj)9VWZjGLRA*fbkV?(@P(4;M?KKFLgf
zV*5tlHj_TAA0?35>b`=(hb;?Zv$ftUgc>Pg#B?}bdQx+~JzGXtm`=ubJ-DVu21xBn
zGj|5ajD$x7Ob*^V>!+;F9Qm12J6PeeAHXKnK|udRf2~)*ZuXhKR$1!Z8xrOGZfIrh
zvpQ&H|22amqo!85b?b%clCu8$UQW-^2W_&yDO;bMJ>9W%`m(UoQ1u5O^EepJ+uQp(
zFvx1 at oCuAOq<cZakLEa%8>1NQtBYT;9gJ)*GLZs at B>@?2l+!Az-b7J$vyFt#e5CI7
zi=T!S&TJ86@$vE1N3 at 7uFZGhXb{ao(R`Y<(<6b?>d++%8zU8L(iluj~%O=l|PLNy1
zaDxu`gx3GlllSgRrkGiSS7(B{#e&WZlA0t7Sa#tk(WT)G4=JELDtdZ{o{PD^94`Ok
zs2*1!)T|}<2QPiDc23><*<1Y6_`+<|=bL~C`;*m4fL%;XSYfRUd%QeX0{GtJYqhgo
zk4;k7rrYXaJ1t6?S`zv9KfUJjQ50}jFoI0n at yGR^ST4X0{B6K at ktanG4GxE8F1Sp6
zO`~A{c!4EAZz{dWbKT*W?f853)_~IPO{V3)jOPFa)2_%AZYIg547R08&~N<g6;F}Q
zKcKd%(9^KRsg`K-D4ng=lA7lb`PSA}{_x{co~5dM23si!iANQVGe2IfE($0A-4Y_r
z^Z5A<M+h3cbzA%)VQ}r*ILL$>gNa33zklD2xMZX2(12AyX;NNy at +ZPBJQg$~q02O_
z at wM(0xoVuX5TG<BY$7^msiym$fMqpQY<HvA47fA)|LoP_3j>S}&Y$Q%_~w+L6V9ns
z4gsEJw=3naM#${oXfp5C7cOquL at 8?9*}(HA`ZkE51TKwb_zX9G;(cX*YSdqD6DBR3
zy!>JPXyv`3o7T3r^e0^#8zSjDUKomeo!+E)m<=>_rni_S at 8}6>@8IQS=kEgpm;rH8
zC0brZMJ2Q}Gt*M1vCG0DFr3YH=2`O3(&FA?v--xyMjK{B*YnApWgwS<N2 at GF^Tg4e
zRM9iq`l&;3URhbs`~A9HysF-f at Nn?fI}unpw^0t<OM&Ac{15Z_=YGOAfD7GdeZtwv
zqeas(&|FveRW3&-5&%5$`O=`*@OfX~cVF8IW&=~i9ORV^DxA%emrDG%U1^c46>Pk<
zObDl80lZCG_{sxxn3erUl~<ey6|Y>c$Nejlx=LY`9W!N8p7WRgCbsRFNG|+WWvMn8
z-%}glw;@wIorX1cIGE|V*qd8 at gaEEqH*c2NU10od&tWsbdMZFe|NUu`^l2cI0lzmV
zcl>~uUJ|7}Yuk~l+1(o}=95SPd~|evJ{n@)s~aadIf=n_tG^%6OmTXl)@NvF)EM;a
zfXS+|ZN|+`eX3Tu{%U=J?^Rxcz}5|queW~rqFip*<NcoJ%{ibvYy5#0&#QU<Xz8Xz
zE^Vk;WA6qCHaAKWcEQL&E{HmMXE|&eXrG{<q3PWU*uQ^&i?LbRjhH4;-j?tuqB8 at D
z?UQ}mNA))r1|Ey&0nssWWYl^K#DJBZh=@qf`;LmONkKtDzV<VfBT|hXuD6A`)_%=)
zh3nVmE2jJ^D$dZG%20n8`A#-Z%vw`Hfk4u0 at ryxnUexeUBU86l@L7=k5rb7;Zv3IY
ztZv*G10I4C8?jska34D^Si9Vz)m;(HJu2-j6p{a<*$LDj7+A(mgGJU`?h7H|;aEHL
z$Wioihlu{f{=+$qMBqZibfe3o^}czTFVhpaweCcK<Y`M;QI>xj_-D0Ne^yL at e*EFP
ziE{J$u-h{W91HO$FZ^3G<T6fj at 7_I8*)p*Wf0vj?jUrEm at -qJf8(T!LG;nLB%d^Mh
zl*<==?Idq3L~|`K23NZL{_G!TYtl6JB^g^)AU4~JEe_RCuy1mcKOf2w+o%#4j+qrc
z%%vUkOU`>ac;$8FM<&a!y!V2XuDk%bZ3mqCQks_9U(K#z{j0Gym&Rv0o?%D3nwlD4
z5pt6jdNFLM314eio*4<^;TiD%Xss~*KSLVAj$P}=o?PaC^=oBC+sx9gh*EoULDppV
zab%@n;`b9R&CLqHO(5heD05Cx&E0w9tG7 at J?>u|Ua(i_$|LV&peG>{A1`lXFXW9us
ze&}V8n;81y8K3j~cVVFHfS$}fMIdc{Et+^F5509V+mPX!N_^xM*Sz~FT;d$aHyq*h
zc|p+p9vH=ujrD6LTkBm~3d$%tgJ0!YF%EkxO^R!4YqPRPpO7(1ixnP!&}s9oO<6kk
zr3%-y;2N8mO$&F1zmCO9?Y=5(Jq%&+Y+-9>V4fat{KJRYu?hDc6BPd5nF#ItyYAp7
zQj-|00bH0g>w$fQdNTOX2Ve%LPMtDVBdGHaD$ca8{XX;m0Mtt5qE71)wt4}Iwnx(w
z7JKt+>ZrQXrAy at 1)zzQ3Zmmc#HWs`XYR at yZv<M`nx1GsKrm-}$Y>LM81MW95=aa?#
z_I7<}>7L&Wc=vRU_V;XF#`5Z<8X!3kNAtgXe`bDEmhE^wlDIclTU;!tnR-t9#vNZE
zVK_>u(l{0%qS|{!QKnA!-CduV_DoiGKC at cmfR~`5ZN9xY0y5JmYb9$gTVSv<r*nLI
zIvi(T$;q)`u at tZj1!c^e34R;3KY|QA`{PiJAKT*=?aP;g-~ZiXb{Kd=OlLmVU0@`4
z^Zc*zu`zk&<>h6bQYM`6(eqrNl+L^@U1qY@(sJ#8CDK6KPBXa$mr38cbxU6PpVro%
z*(E*MtIx0UW3G>nkB|4t7%@)ZpryNW=T7(g$+95*L+(a~hL6bjw$i<dj4KJ*EjEaH
z0{`sT{@6}d`}~~owZr{~wO5meuUt)kE-CR`uiy?m7WR-zPEJOJZftBUujg8#nZV#M
zryQ_glW?;#>t<UzlhE~j_Oll*T&N!!O5bRv?|<4V{_X8NA5FlTvbC(4VgI|W!p!Xp
z1KyqAC#L`HdMhj}{Gt!<ps`94Xz90GLNTWv5Bo=DBnb$*+lnMB{?Va>R{)-?=<Y7s
zt3x@}6vNEQ9+OAN+&OgB#J=wpCpJV&O+ALk->mHCBqi;t_lEqIY}J)ABpWaP73-<E
zRG{~<r{|GAcTI|GrHQl&`<?Rgv+2 at 4SzT+I$!Bt+i?+7B%Zu)Pz9ME=c7qCtxZ!}t
zRe{I#pFbC>e-dEH&zC7yG?Od%BA3iVU*2K1`>X$<L?OKnj))KZ;|ubX9X1!|7{bPQ
z1aiOIswtX0%ffWd`RPs#*+Wh#DQ3JN>nXf~K>)sD#V%8ZkxcmTUyDK#s-wXXyg$5&
zy{r1_Kpx|<t}4gDatByM$|GlpAa;E_*P)wormA|is`@o0>K9ga5FSTFC9YhP(Mp%r
z{+lQ`9vSTTXQ%&-OEOblH)k=>*L8rL=s<36Ze<mfM2QayDZaH8wV(e#vfcu!s;%oA
z$4)>%1wo`+Is_>tBt$||Qd(N_AR#IuAR-_lB_Lf&NVjxLhjcd_y21ZkKF|Ap-#7ku
z+%fLG-gD00d#yQtvG!VXPTY9ZcX|2T6ls1vD(rDdUA>Pm+4lJfEl$#|Y}#K-h3=B&
z^|@?@@rsO!iL%$eYZZLtB}1<_eSEf#4RsbUI^|?!hRkxP-7xipamf?C{mpq#h+nc=
zwYAbR<L2vE2*kw1x-U_xPBKr2oY?<DgT9Rl1WiwSy?uLn%f^9Kp?@qsKxAU-Z3J5O
zu#5WwdMqR{QB3s~sw{3P6;mij{MXoE#g8tpUUo1|r-Tn}fAQ=}zpU!|9iZ;+Ad0~6
z- at mue<C|AKc3TCFdx~IubG}P6+0+Lcb_$h68ZLJ9w=SUm>-_g<XAN?D%O*TbORYmV
zX`O;%W-LWM3AEB(3qGU^gQRQrk}RjQ9T(NE5d5nZ>~Xxu;FIz;24oC{#9KdN_LHw%
zi1J50{^}f&8P?Dhe-4qWF&r>AvODGiMV60B{89&<M=x+S-%FwwO>SE~>JRds=<4R=
z<e(NiO_s9p7P?Q1TU$3ZPv);}Z`V!K`uUp&v!2Dj^#K%BhVkcban6!`8jbC|dX}36
zDNkMezvk~W9hAi`8}iNf{vdmYN8Xfo`So8za5v8#9E{v^65gY>3qI$MkB=8Lu<O&!
zz$xkN*zlXd|Et6R_0U4AG_8p{jzst;l{`agJw at L4V&dYc;4bQ<K}t+&u62U^u7mXC
z(PVu9s*n;L9Sy8U_vSGfStMb_aF+flA8B_ at MJFddm_|;=1r>A_laG&Y0ViJ}Muaks
zE15X0lg<5WX<Bs&APCL~p7Rv~Ho(IN8z8rvdD2*1e>bfZJnJ|q at irxeaVy$mocn+F
z0`RJ~D^<L7 at 1@^asGg1Su+A at GM9yI2Oy)Z_uDh<=mcEP39*zzUDtePS9--D>WoZeW
zU0mWokfKc&mt_YLjClGCdrhE?hQQZP9_mqSnn|l%4O5-LgoMoclidLu18Ty!)5q8M
z1VKnYr^5e*SraALazn>Ju%I$uqn-3_T9s#)8=JnEi)3Kl*lX(St^Fs*>Vmb=3sAXe
zq42y-sgF0!>`G!H?Dkp<c75m9#X|XlnY!6SDra at F9l?=!N^h9Cyvr?6?E_qmE8kw|
zNxzsW$j|@m|G?1#P(G>Eb6=*=!P+j>DE=L={c8LIWx3nnsxLaKs$~3ZobO7$FM)8I
zWj;p0{$0-|ZmffA`L5`-{e2nZ<3@?_6#wIiioT>4GqBG-=e6=JnRd$N*X147Ty>f)
zM6!$;2>~Q*Sv at gR)6 at EYej2ZCZoc*OJeBk)Qf)%w^1}x{sk_9=0{r~1d>jVuQ3?p$
z0FF|xAnFmt$H)KC)qXB%!V47OYag^_D}GLim50Dr;_M&8h)zdS$^V6#WD&HP32g}M
zkfI0)QyH6>K=0F2Jdz&?>uKv5uYE|ec(;AZ4tH?U7s89(wv$pl>smY~`NNYf9|zT^
zT;&!g-sD>iPWZ8xKV>~aJGf*+n7=1xiiPg$gktOJ!Y&3E78Yg|H;nZEcVXf|wyKbg
zsgcpIy1KJq_uy|3(+e`%2yH&oz&N~CVU~=4;NN69k!<mz>MUGVs>EqoBTp#;yrWO8
z0a~+M%Wb8z?xH{2^!xW&rAEnt{ecb!wyquz+^7QsndR)xmmt0UISuRP%JzISmO0()
zZhpKe at c2Oi(=2GKH~2}3iIu~{<fqQzbu$TzyVc)B0!biTFqWw;bzo<<Erxwf3pACg
zLqO_3O?BqX8A=0;NJ2JWUS}xqM9PK(dPR3z1BbdQIp$^BaVwzp{Y|G68lb0_mY2bi
z%YJ3F`B0ef)1y|O`c{`3P4GtOULboe9cTcFnY>hzn6=f_Uriw_{^mJxhiW+{tz6zG
z=OT#1tpCX=tARWVZ`;W_TZ2e5IkOefZ?4~s<|5en_Rp(uls%9ibe%pLY2W6IL at PQu
z{r*Y24O5N5Xh)qqFWQSuNqtk;CAVg4!u{6&5mNsPDfqX9a0tw_15DXIGs$@D&oh9J
zwU3pgNtTa#Q&4~jr{v_}$(0aS8obCTC|J@`wmeuMPD^MueBNz$mC8rDEbOQ~R-BR=
zm58bjpg7m at HwH)VhbHe80zx7p-UX&IvY<43-4M>DrKNkD`P?ehK`G~=-!JC<{Mor6
zkXKZJ!RlTi05=gB9_~9dq`hUG0>+Pd=g6+IJ5?E()(qn;F=uDz at Q*5n$~h*Ft`Jy{
zmjCD)O?CU8m6bK*%DlTYT6{Lrtxb?C{6kN$rp15O`?J54i_CQerZ94(w_urB+Eg#?
zyvOqYpa#t&L3hsN|8jFIK-1oudFI()j>9+&opa{B`9?KHf942nwi1Qxxfz=+PNct^
zp|F~iVMB)&`m+t6Jgfky+})iFc=9j?SFY%AM(eP~=tZnWWaHI^&?fXh9<lxXj7}S%
z^V~DVRLfR9d7UNHYEC*`yzUEE#g7J6CYe<|B-VcOXK+s2l{gopwl2xZ4F9%-F)}i?
z%a-KjIeHZrGGhq|3GYduig+yZ6hh8fLpv?J1Em=EqyMfXUDs*4*~8XyZCN6%S+JuP
z>&Tk9_Y18kgP+Q~Jc3RY#CROv5D^i{%FC-&*K_v*3vSEmr~Xf{FlTFArtX|q8H8j)
z?TNwCvaQ{Aiwe~F3hHg^!iid9?9WuGSjE+!R>?o;bp{IMJ}7f3QHZ$hUeVXrHy>oq
zO`Y{yOurr7;n3Rt@?M6%tB~#P>iFD19*f1i#ijp8i8u_ko%!$Dw8cETMivg-)c*?(
zXdn8e>$jiMS+SvYMAW-5zMpxS7 at a)a*Ier=<^3l at 0425-`}zt~6#CZR;-XLSn03BY
zB})X!s0T_X#O|Q+a;5eJpH=i^?+-0X7gtUHSf@?jW``|3@!4_>P1noL2e%Z^$>(2x
z_hw^q(xiHB5&6~L9Yj3T#EQ>bxr3I5Rx1f;9-udSZ84&K|AB at v^A(AC?E2xJ{g&A?
z)ijk#AjO`Yu>TPwdNX5yNoup=52U0*pN##wqrzaUSL|>B4D>mr#z(yXj!DOS04e7P
zLY_<+wG<}sM67TV1em1<HSRULH4+aP7#e!c%{|L7);Rt9_iy9tk~#iKZm7iP!%#L<
zSb&lm=1I(5A++?@gUHDFywjFMq@?c^ME2ei4a%Ve8QIcNRl&@WvfL=1X>PykyS%Mz
zyyNJ5Zp$i?#PiIh>siI`?%lgn_mSN1+c!#i-nqN#nQ<=Sqb~lwd*u5`v6&_!AKd8}
z7(9JYG5Czctao9usG4+RV`G8}^TW}Jw}F9ITn=|RqeR>zX4+yDz~IrDnwp~XqW+}3
zva_3z=}p>^!mfNLzFi=qZ-+-`x^pKKNAdps*L6H#gv4RmKY#ryht7`DNPP?}D-#Ap
z1;2FV&!0aXVD#sXj}FiW%FD}DN}Q~s@(2k|P*PKSiQT{G_2#oI9aD(xmLqjsRh8I}
zXyr)slDWIk)=>80L$vR^qu6nA4j6fAaq%HS^u-(f=p>a&nTsRZHBeF_n3$Mon>K#5
zGs-|6SLV92u(k6zHMes#EZAd10NXHa$IQq`39Q*`38;e(DqGBxc>*V1#H!C=>R!#?
z&E_ANdXnM1w@!yn_vUS;tENB5)Gae$b=PU1AzRdPT97q<QT6#LH<sk$;IR%&$v(E?
zug1d|{MFR|0~Io%rV}gyw}j&WdVtpn+}z#g8-p2)Gq{yRKo>=2aC^lbTcz2Cq^GkG
z1j*EoxG@*_DQD^m-+g(=j}E88AepU>RVFAh@^@67W~NBh%wuMmPv18L5nLy`@0Q5*
zcPdV9%2mhW`}U<^S~|L*f`U7JT>Q!3zP*B^klI at 4iT5R9cntS2SLt~!_gM$^zk__Y
zGOrjgAJ?j+tr!2?mQ_=`ZuRG<SFzKw?8wLn&&xySVuyJ`5E|`cm7Yv&Y#*aVJ#=TV
z64XKRfBQ{F%iU>1n^VoYk|2|5dY1_Zre|k~ItDS7p=%O4?TP-)Z)3QXD0)+sz0o;I
z35nU?QC-_g&A+@~S{qhxJI8r at pR?Hy&GTaCBqf^MuIsEt&6TI-=NmQmVX%44DNc1Q
z6N~t#KBMO44e42XVmP_7BG0Z at K2_sX@$!i0xPX3>Rf?AI!f=tjcU*k at jgA%`cHL5r
zV$mpm+c$4KJsX>vno^wn4By*onW-2)VAaYu>cLYcxw})_IbbLufrp2O4#*;tL-4O=
zgsN-Wc34?i*>8{Z#hW<FEuLS-Jkhz!mKM1*-Lm2bTGM~{YA`LR?2qmTGmvN<-~>{o
z0&Z>2w30<|8lIix+YHCXKRlkSzWEit^q4thYG$S`0+*egy{Wl*((RUwq{CeMYagGF
z6rvd`>=!RwP#RS~+VQ$XJO at lecl_YY=meqRg8qNK3EKU!zP|o8n}cS2VnWQW=&QvI
zBU4kUADVfO>(e%7+tf$9=-9J5W=t$tPiQ41CI<QWU1G=&Pl71>y0t}eO1#f4`w2s~
zj!jtCAr>rt_J9=IH?mu|Zk2uvV0qKX$wWVcnTvc^9<j%<ok*&{yC@))sBV?+ba{MZ
zZQR$_my){w&!5!HRHu`SHpCKXPcir`EG$<D?Ck9yYKyvTJ}dIL<GlLx3IW<S-wl2X
zNMgZNr^Kliq`4n18Q&`(4x@%ZyRDl7b_q3Gg at ycA2p0M>@o}DY#GUau|Mzf1LBK}*
zV77Zk1ZSx~`;>$YW?^G}{Um|@-ukrWpF7uO6A5`OdQo%AqAyh!(LuGy$VmAT7cT!M
zVjXSm=Q}GpKmKM=Q?R%Pv>pF=q`9ADWr;^2f_kXs=JNDfexMM&wLFk#$m;ISh$Xv=
z1hedt#6=v_-(Ne-`cgV~SXkO%f-h57eIyrbN{AiE`w7vkIjE{sEN|5LQDpBGE_yG^
zUcUP#Z!>^g at GSiB=g(JrElI at YZN6LldGh2#f(?3;#gg;GFY{-R2TyHq-Aqrq`W3%g
ze%p!D7JTwich6*NXqymQi5NCb-x-}?pl`mUGbop&SfNq_Y(jP24>d*4NaPShOH?;*
zr1seGh6EJ58R}##K2FXW{(>XNkdcNCnf;Dxme!Pe+BBU{UvlBXg at 3OT_2=4S6`4b>
zEnGf-K1GpOeR}w-fW2aYwN7uRI^$Jz>IH0iZ3M1+$4nVFxgOUOdUHQEWBIP6NB1SJ
zMKi|!uZXz!{5c0YQj~Z3H!9k`zqP28uEyG6#Pxw(@VbnQjCM1Zz}v9*gJEg+J<n27
z3%yvEm^|w5*8s<-S9VIIV=!u^1cUvE3X~N4<u2Id9wk50eE03H&Wp8)+OvuAe$juA
z4-}5HMEn`d)AeF(D<|ge$~UUGr!;2pZ3%tT6N-Ph_xK3~tvZRJkS%tkEU?jtQ~}FD
zZ&~|HuKp8F&g8b-)_wIPKeQT86(XB$Gb+++nKF{D&1Twq_M6sG#v+ErKh}_04xN{|
zLIBfgqEcc>c;kkz^>FcxS~atZhTp&60r`MJe^_H4;r4K}d<n=(oH^u;4;ZdbgcY3K
z?BTEcf{RPb%5L~_^6InMY8jJe=#-pgU|_gH;IuqooJH<F;J&|`NoB8h`-iaFO;y3h
zkrCa at ioQHywS8I@D}Kz%*Iw+oF^^l_Z|QcwmFMHGk=s8-{%w7ot9W?5ydD^cGDMat
zNV?npk^^gwBe|a;_um_?c&JPGeTnbeI<r$#sH at 0efsJYFyI6FZQ6fRaw`+8?qQwT>
z=sD^)0GdAM0kfoh59&TVY<lQ-L3sa}QM^Yfzgt*jk}rVVuaS}Rmige5OW&H^$dghU
z)b+~UE}&(zy!{Ba7q9*K?hL*NlDT>j9ehH2aOIb{TmNezGK at dJYCP8cmBp)9xBbYy
zw}F%Qj$gBz>J7AIHP@`G at C((d2Wq}Y=zDs8e*UYO^K2C6o=QGeC#I-HuGO!1x;WO1
zLm+gpAN3DLl1Bd09R{BAd5{qwZVGhvYw~nv=m?_nd`wIiBqb%$-oh`ppPZzA^W(>l
zeN5Wfsg>apPzQ?iQSWc2yKF5`QoHR8V^CWKd3ijLOFiVR5yvZ`dM{59#EN_4QBvR5
zDOPb;NW at m-i=HOXuYQL+y|A{kQx7TtEy^Mt_3!lvTKK?O1;O{mXKZoNZ}jB>u0piX
zeXw$Xun0)LYZc%DbC;{NrM9|{S>ZgXc<H`nGdw)}qL0MS&kyxV(JghZ%)6?F4x1Tq
zJqedTX-GssaDp105kUPWz40kf!yo|xfkO8KK44vMKC6MxyOHnRzHbVP92AIz5a?~l
z8yOk-J~%^GQ(rHB`}$- at ywvxB4zKlc{lR>z`x0}51?;-zZoVMLZ;HOWnP=Y9$->c*
zFBI_p{Yiq#)>d3}lonX}=g*&6?gvh2vz>&BL0lMp$w0TSTz|**AOFf~Z+t+Nwb>Yt
zpC<8HS*TjE at m-I?dy)Tt#3Ehj8>kDeGpT%YCgn*E{bH~>+=XA8$iW9yGC6~V={qt1
zgI8{udd!AX4MFGe at Mbj68h#+<y%=@Zk=b5na6M=lkHKo;YSJKK(oOY8Fw77urnIc|
z99wo;!Na?|&Rh5Dz#_AB3m7+D^)EK>%V;%w(U)tcjA{d`l5v~<t^vAeF8QczH6j?i
zR}y!y|JipS?qPTih?FY?=xsYp8)|y9x3`DRa<oMYpP1YJ?`lbgjI{JC3F^v!Cprxr
z&|7xf$p`=4`5phjpBclIeoy8<ug%S?YC&|d$bn{{Vq`pz-j+O8R%Uq=ihJ3g7>4~q
zs>;PwmG`0SdZdrq;FI?yRyQ}NTAiuCmA(9r8Cv+%7r|rp2DP?cTU$fH3u!cEConNF
z(M=S1*J19PyjG-(ZY0L=+5f^BCj-Mdv^TEMmd1iF_w~Pb>V!|7hN4qN=9(OAE8pRg
zDdw50PEAi|n at ob!36!Sw@$)nM53Ds~3=QSHm{fv$`gL(^<j@|;!oot5#IJpPqUQTi
zRPKKSF)Jzxq<UFTU+>>3{=y)qtf+_s3O!!_q{r&m2_JB=7rrOh;Mqnd%_{y8_Ba+o
zdp60)$Q%#V+_<IPm*=`xy6Qb}!w(EDENB0Y5;O$#m6n%(prk|q4~EK4|N3?B3IPbg
zs<d0Gu8`OIcL({OkG&fBC@}SJMP(uK- at 9~l0EjxsC$IR25c5784E;%hSa8lb4($JM
zj`{DJKHl%oOv+bCNE(Wx)+X!Yfh~;r9A_>uF)=OFYDpwy$E<WIh^mef`_;nGI{pJV
z+4y-8BK!AwJq=Tb#v)Uftbp#G4Hxs$%YSXiyXvPuT<rK9gSlfKlS;4Gy63Sp{6hP4
z&+)m{w#(I7sjuQ*QUCO9p#RDA^E~r=vS*qZsm3pIEPK=*W~$v`ZSE{~9K1}|!9}DZ
zA*psE#GT+i!4nCkEBBqk!m-!@*B37}*<Q5>aaX^n(B1f{&fT)d_4P$`j(eM#brM8v
zdxA{xd7Kw6E_Q8>vn7-PA{d)l!Uj}g%FNun6O!NsQ$@kGYuADl8C9GQ=JZ|cOGD?@
z?8>K}+;L@*$|&eHS=%df3H$KjGT}uphxtyra&i6R<h;C)9f9mTi#|deQUN<!urErk
zuE at o7Gz8I<sA7FxY;+tj$A2fwrED^$)c})|nA~U{OO{|6q9P(>4YD;dE-2|Z?))0l
zqexf#F`-*vQ2YL%d?piXFG|K|S%__8(=J?nEsB}%Omx-NQdImUj5$8sbB$(UV|zJ<
z<#pX&stB<#F>z}1Enu%WTo)Jh&Nz<Nc~RblDjf8^-0YAFa4Q2r&Km?uc>wFP-uaL{
zC?<v?^OO)~WhnOBw{L8HqH!ziamu~>!(EX<Kix5?43(y)ro;ZGSyAXU5)x at ZnD0Z)
z^J(Xfa0M~$$BU>Gg|K;;!<Ng)z<?|O7XJ6y<KyG*-rntYyAnHvC4al({Sua{_336N
zMn>tC>BH4(d?QpDf at roRE`2$AMO%=f=fT1}CXU4_%sXT_s(8O}t2jL-ow}#HI}ntt
z*!Av&<m6zD+o)h$bJ_QiZwq}k#ulPmy<2f5?)1hL6+$h*_0qSmD+9ca#%@NOi%J>n
zSv15P*A4Ie9WF{1TEWCJ?3^{WQV$6U3HtKodTnj(R?~qnn`(u%Xvz at aTxUlI70%}V
zVy5Wo7L1RPiOF_>-Q7FOW0NkJg>I(K-rg?<jRjj<TRaYPiUWnV@!HMv85wRXWqU_2
z`!|G(=$)n`wo}56_qAchA4Lo8#bO7e+7e2($_4u at M9_$Xyti>bUGCuEp!n<=<DdTi
zz@#L4;hiCS*}Mxe%yY6O6DxhgSW}A4Xq~;kI%QHKbF)3!kFJrD%Hp7US3B+_9UVa;
zds9TrEG({5u8rcDOkeziGQnVmlAkiO at go(2eRGhM1ULhwFLF9t7ziE9J~|`^4EA7m
z<LkzX_jO#mEiPZaj7l51j%EzamaP8~TG^Ubst~2PDiDLJ7Y9d0MQI0{QAp}q<&IcC
zB-~mlb+_ at qFUrg&X<=nWr=_JeQ0$mK)EvQ~|7M8TRCFolXwiOnuf%bSv{fo*DnwnE
zih?3W*F#TaxX{+~tr!)sjO>cB;}+JgqEC25V;hXHjJUWbPCG-4o0!CKMf7Gq5rcf-
zyLV?*)zzDSe5e2hpdlwGUznQ<Y(Btj*?ezV&X)@Lvv>aD#W>XZav8HIBOpA#vz2?i
z?Xdh%Z!SQ1Y4#j0sl;qs47T%NllE5JUu?kga7n>ng@=TMM8S$j(=mBzOu6DYimeg+
zb$&-F9cNe9u4eX%@9kU^+nChv-)Z53gy&Cx{_>@F*}@4kJ8_lMDb{Dr(N{tYG)f}5
zpi at KH_r<=<;Mmx4;R`xVb&ZX_2Zz;FRma<I8X6=!hn{>V)m2KSGH-1jPG&Hz$bTry
zdkGjMsqVg3OU(0p>|UM>hw2$eN5_Qtc=$!~#~R at QX<c{k-u?XfvqM{(!`46i!8BEj
zx-xfyDP^LieH;37+PL=pwW;OhA3Tv8J2TGg9h0+ejp^1-Tjf-lTch~zrNSYS0z*SX
zA6s2-J?u+*6xmQ&c|T(^7T<od4ws9Qb5E-*hS%cy3CbX8hqgruwF*~@{~LS~z+`oH
zcb8%d!|%QrV<<CeB?WmN-_#^4S+{Wql_;~Dmi?KVOB*CD;o{<gia_asRTTDrU0i%u
zv&@AT)vppPFH6583?udTa8|UjrR4(;FE1GP_k5Q9_pGd}@Q;dq`>u>vdyB3LaC7Gz
z99zyF^!D|2<+O=BeE04hB()S36_v{4$Lj at s^))r^;1$h)q7qV5Wng$vr72YLI!f3z
z|6zrxdNkHyrsc-Tdq)R56<DJadI^u2PzlNJKYqa7`eL6?Euc!5&z?!6j-#2j;D7dy
zc1F#%m;M0SRJo(}S7uuaJ)a{YT+n#b-`f1j)r*ZD`S5wZq^rAI0u=9Nx3U6LLMbRX
zLcdyvv0o>=KJPH%ACMg%ZnPou9uX12<F@;JAWQ$0ql3d{ihgI*;tLB47PRmJT1Q4p
zD-vd<;m at D&eCuHs1 at XM1qVQK0*tq^4R|ECt7v0!;O#D*8=u4Vx4LJ}*cI*$CLp~)Z
zGqi?rdiGVm%Q*J;Eq=M|rb6bMVmj)Z(Y+`24-WaaiUk1<rD+K-Uc5-n&d%;-RkuDJ
zs`m*7QC6-nZGEdGm at 6-)cw at lZ-Lz!T^zqC=bM?}n9B&>To at cYaqujhZ)5XNa;r0$}
z{#K;-Nxi`5cbs<ZA9TN<YNP%`tKj2pR at N6-`zxu(jtBbFw?tol&CSg{kAoT?<GZ=b
z$Xqyc?p$zm^ex<*{DIM~D+0y#vv}-%B?AMRKeMvFIWG3Xpv6lCd{Sgg5O&>WOeoEm
zTbLcu+M3dD+nI{k&5}|#EFQIAUS8h(V;W;ykqJMwcW|H#aymdHc%}DXC0A#+y`$rP
zWo0FgLCyKRyYnmhw1gLyN6I2Ct*m5d2|FNUTt{uqW;!$?W%mYD0EiRP(;w!Tw7N`L
z?6pTN9t~$!#QY8p*;yX!o3^94bBj9eNsdVnT!@#4hq2X-TB&bQ8na(m#;YLHorl$Q
za^m};R}l?<(QNG3>x9_Y`zXk#rX(^ej_-zqhW=gNnQwA0XIKtj(bg+oKVbP3H%p|Y
zso4P0jq0Wsg>G at Io5ycCX|3yT#g`Z=mI<6~#gvr|M(|po&j4jk>Gpz^Z5SHT&S1Ff
zxN|(xQxPdNqv9g2r9~DV9-cZ(LPMYg)}8gqPl=UjhphwWioUHnjqP&xgF?y4$U8Rl
zaVu>^n at N&>*E_xn*iUKJ>7l6-5)zu)K3-_IE;-nlb35-P#>K%wNlZ-aGVVoutEj^S
z(>!lhk;$-CkZhs1FXnMuKp<T0yT*N*2Oq{3B7%d7&~E at +0y)Dd#kn3B4sQV*K4&OR
zug0vt+!rHv>vk1wnCvEZ1WNE|e|6&29D0??vZ3S=3ZWZ`(+)b&C*`1TX{Duht9K^=
z%gY~?+{tWyTlDtr4-j#T)YM{B(~f;CnWa at ntXTB|7G~zc*}e>&gI_yJDkTZ&ZKA&;
zCRS>83ymEo4qQ8~AxsR<GhCRdCDx-=P*Cvd at jA%k%+1SdtgCw$?Q#5q`S$HnY at 6eB
z31y}TM!ngi-SuDpY%>cBWjD9ds^%}r3k&9?Tt?^1Haj1>d5cMu362cT+uD`;Oex=S
z&H39+W|8yR%(Ao#9;C6qKg`_Rd>7Aa&ku+PFmKXPehFX~)pYCZ=}A0s>TElAn~*p-
z33?nJ=hZP+-N8uv$HPZ>jg!JI_N)bQ-90@>(9Q47#?12KNji@)4EOdvn5^@USBihY
z<e>OQ36DiR!_6pJy90t)c%y~8yTZexskL=l at TS+?k`Ln|PC(ix(<8Mn*pG7b at n<ew
z5A^r<7bCiZ%B&U_7kfMuF)U`Fq4C5C%**2)ja{YZ<qdIgbi7uqR14NvMo%wBEkips
ztLBMn!exI)Rr(NFGaw6qzLeF|K93^#g6>g%^C!FWNrhSU*`LetOMsMRe~%Anubf9^
z6lVm#>b{<0UHG590K{Zu4{_$|{K?$B?eHasl}X9SiewlZJBEhj4C_9i3M|=9m#Tvr
zjd)Cd-y{`u`Yv{T9fRt*nmv2gju~}N&f>a}bcdIZ57pdk`1R|X!UqA=9aBN{sD{oo
zQzQEgn_hVfMx({>sa4v67Es6h!op)iLz>apl&HG82Oz!1uP<foNo*yiO}=t#WuRwZ
z2uAJ0H(KvpWC>PM0N+fRt?@mpC?7t}#l;oZ-md8NxsUh|p|X*YL~Cm+AlxGOYH4wC
z at nwY)Mfw*nUeFTyGBPko#ZIH0Mhdqhl&`tvHs3N`zO)$7*ro?zp9fy^XF&mWI^{Ed
zx~IoZp`%n3YMq1@>o6p8_^PU^L9wy1RR`>752|mi!9ayag;=w(%LCQ=+$mIKkPcuR
zTYkU>l2fL at W|Ur8S$QDa at Z4xDhcIn$V?$qG=oeP4It-&70R at GEL3Y^4i&=6o3M8Kz
zjdHDr?*hkw1gOFAx12(Y{z*wDp?rkupHbl?oWP0-QTQDU8g|Tzw%zmRiU*!9zPCOl
zJd$u&>}9ocbWDsD_kL_(5Eo});H;*pDVvm(bo!ndeV_G6sf==xT<puE{jH+n-QR*(
z)HuP*+k1ojy=LIQu#*P}LM4SaH#e`A7i!Q4cYLCInBwvG`0BN5iS0joUPD+y2)9o)
z24BEo)hqvs4Rp5MUDaRc%cKzgd$@5?;jXNl9IcLyPJ|%FI%Y(8SHJ(BQyW07qO|mR
zG`)6qT<n}^f)WxFC3SUm-KkG_=@(NoFnFU1hyw!yn*chi<R7)K5)%{QG&MAg34V5E
zoQUoO4*^P#0SDNhvhU~BA6Z#}`&)}rV22(@3>Vxu1`^aPvZLF=j>f!y|Ng4(alX}{
zH070lDGMG($$9T`m$HC>fRT-j0zEzb=a7(E48L7Zf4?-y7dl)Dk*_rdoWci7IsaT+
zwSmhC3*}+GCr at senVEI0443TTNiL)Fmq36k0v+M43~3CiH_uJEimZ84yrhg(ltynj
ztC~$zVqlC=t^wy|WpQ|YIqHz22!eclXtaRpof{b&OUAgZUl@&TXnk|$QbSu?fWH*e
zD+y}Q$P}8z4yjemx9&PHT8)<Lj8AjzB+ohhnapt4KjPdg()0K(_~rH!F<`^8n<4A^
zj;q*P1uH4RFe8D{(b1X(ech_c%0!UiAL$jMzpri?afN^A&D2vk`TCWv`<AMg^_Lch
z80L>=jSn9_yo4(Ao<4cPsz2*B_W6D;s3+v4l#}Jx$S-!Cvgpg8qoJWm%gC4!yjmpS
zyh;gy?y}y<kXl|IjeQ;_k_P{bcAmHZ!Fu(^jYbGo8q3l0C>TU`-vLl>==I-^A3s#o
z)ur_H^^KlBy@!J&$Hf5eqKfTpC(Tl4PFxa>SD at _o@T912{kfEynVGz|F&ivR8;2IF
zqS_N??g}R3pt727-r^DxOiWCu4D8SBY^8(RPdhZhjdP$wXkOpD2QFw*@MZ{o+y0N+
zqfz_&E)ha6xNeY=J{A)@$<B%TXP`3qU@(r4upS$Po at vD;w_sMc8c#Px31UDjokjdm
z%DN&gBSQtbib}Y`n646fav7fbBdq-Rv6#HPJTiE9?Po4$W;@U)08 at 0jV#|`hB){O0
zQ|GyJr>g{8{*XGz2Q(rhJ9_{&a4HF0KTbO&Z6k(pN3lkDdSyH0F54$j$Azn?M&@-^
zO$iV_Zr;_a2|s at LU%#z^1Z;9)A^*q(zuPVc?hUTDmX?-tAlFK3D>-zif*Vn(YLqtM
zjZ_wEeNiL0ac_#wu}V)F1%&`z*Ja+cl$0^S+eyUPd_A5>q at lNc<<*LUh=bswf0zH}
z0G!5~Prhp6irMW_5Dh|J6Nu=0Or66MNd6yy$}ok?19=a}rwej(QSsoi{e|?4>{`R6
zhwIHqd&8`{c~jp3;zkvFv(6k}%+jwu%f-i+1a62Wcc`~l4SW--K<xFUStyKp(6}sb
z+iR<SYV!V3b_S{FOQEv0I<hj7pmhVsRowtBO!p)3OYD&}oWcx2(v1B4VJ$5!&g7#b
zB^dVk#l_&99Pa7a*^TSxHlC;PCDE}1h9g0C_Uu_%$0lRFe3vcr&b~fu at ZuZQKVTp`
zO$nEX2nic%YThm_Ep at w)lgEEuUtfPV)ewZ at C!Z?!;**M(!WX>60F%ANn#)`d7goF@
zBgtSqnRu?x90Sjp?QhN_#~>pk<K`XDp|jwHT*HJPyR@=$@Zr%>$c%$=(hjmv5+_ca
zP<isC(c(4$eNvu9-zON+)^Kib>(TO73n90I?LiylZLhOwOM}~oU;8xmq#|UzB>2-O
zvHbjeBWvr7t9|n*;=vdr?6n>)24rGBfrHZH<f&6kbaY<W=U3B`lHOfs*Oe2C4MeA#
z%d2DGzrSc_Zy*2q)Va<8(c^~eAtPS6MwkGAA5>rVEZ$9N)CXs4LA6|*nwmOQMOF~A
zk^m|u(97$L`_WdPWwKsU+cRtHB#^cdEv>CcKa}sykbRO(bn~XefQQ%wpR_?<Cw$db
zf#CdUBTGv;fCbgT+BGm%NL6ghylXi^D<?5A6e!=!&6!8X+ab`H7#V#es0qq;C;U`Y
zRckTQmpAS-Ha7C;SDglLnE<z|VKsOHlvI+so_omt4psqOt at 0d2%GSz?`wa_a&Zf73
zu*9l2&Z*Sx)dVvrd at 3&&@uv`tcE=BsYHDjsww<gCdj0xDZB5PU_4+?KrZO-w at RZJe
z_j|m&yn>`@7l_D0B_$={-wZAtO*0A!eT5q$7k#Ncj{1-j;xzsKt>*lv<%sF7Oe8js
z{lnp9 at Gxj2N_q8DlEn7#=e70pGIBcr_sD*R&}XkTg|L9{egqD5vscT8sh0n=J-h0S
zu1}%BOI?vR<ubrtQ<Ib8iHR_NHHQkb?}&hC+zpoQk)yMd?$(B(kRV+(zC<!oQr;r_
zb2LskS7<PxQgY3@=t1qozIuh at Wuw+0R8B7F6bSqg!lM4z-Mvh#NyufB at yyw?Xb=E~
zf9B^W-5GUL)}Q9a4~>d?1P0Z}+&t~7JHO<D8apj3tN+mOaFN#+2<Y|qe)H8J^Nu#V
z6hN~+MJmar;^+%q{ZX4Q5d$i<3)A}DA$g4R&<sKW1u$Ir+c(N$r)APnD%-fYxMyI!
z(cD)YZIg|TbI3BoSiZY_L(Ig)#9^tQv%Gp7;h#lj*#p2oH+T0xcqwuP)6>&vw}P((
zwacXcozHf^(onN+mw7t0phiSQv=28l|2s<1&AXPvPDxKsK`Y;q%Fxgd4Fj)5?`G<m
z2Q*S26HtRxEC-1|V_O at 8tE(#)4^P6V$KNRIbCNTts=*Bsl0Z*S&&PSH##}f4JtKfL
z5+hbag+ZjGq`P>2`l$Z^Qm7~I9UmWm4hpKoNZSPgIc?0gG2r6jqHX{{wo!R&H{L1n
z^TjkUzWLe4pj-I-wUDr|%)^I%XrdP8=EekRA2j9`6m$XIOM_-Z74wPNwBe7D<OONd
z8P09Gizhk#^{(T4fKX)pv5Hq`Kv>g1dh`gDR7Sd1^mx}30?}rR>F(VK97P=+a`tKN
z%QO_m#>S`bG4UN3kr<;sumGT&D<xQFRgX`59(SyVD8byLxtp3Yg7J26aA*(Jdnxna
z!2_OW?Ubm;MP7dXI{Edkdej8nG{*e_pd*sNdhT1-z$Sy9IgU$B^+g5zA$~2bya_EV
zEaD-)J^|gLMH&F6=j7z%n~e-HiWM)9p4IpeST1=7MV>l`7g$szfEX*^dbnvuNkv0K
z;xr<s+pMfiR8*iHNTPvat;KmnMMareSSAH&y%x9Cl%j}6KzO2ih~%>}NZ#ivi;Iu<
ztg5bV{PQOqMgYl=WiAW_YD&M9(?*B`iji&bbp?KKqY>)I5ajQV3+Ug4+yX{=M-_BL
zoAA!f3#U(_O&P`}ChO#c at wA;?U1kHhX2`)%{N45LT+xLYz7M!Zd);q7F;q7WK(bui
z;IlRexgWY^5ojH%t1B2R!7ko>5bFE<qhMr0{*UW&Uf=%Eit2}bSp|h;2*5x97X-ja
znitzno*mU^8rof(oX_jiofN$M;A2E&Bw93r3qKpL@^ZHeOwVLwVfp9Tz+}M>D1yWq
zr5aYZx92`u)cZ?)>eMMjks!`~lH1(SO-M at WjFa?(k?RQKH2iFCpEW`qBt3^p(63F_
z6XB5FvA%f(++n$fTg_2wu;KfmG^NumPJ%iXQ3YB|>gWifrlHxx^Hn+tG7qq?VPqs4
z+~E91o2Yu4(;#?Sv-WTIKY#jEg<)i-VPf*#+}c9-?B*T!92f#2$wge;X~C;`Yw#5$
z{&30oR<HkSE(&m58q&t`!40hzfg4CED~rM^k~~O#>+Kz!pU>AC#wnhP_dx%+gy0i|
z$Dcoclx%G|N5_ZymIn*wkuHN^LKV>60r at O*#2$<Ttx0MXSkrNHbHk(|HNvudS8k>P
zdC<L?NHmbc#g4Lqqoe9NrOrX1qoje4P~l(<e<=bZ#Eqa=_xATYJL0AO)bX_#LZ~=Q
zH4v<CZ_|SeYz}2NU4G6B%z%6G!UZaj<X{dAJo-fF+1Ng!WY*G(M5n#~@mr0bHG>%j
z)iFl`_$)3dTFl&BZ)R5pWoT1Zw at Cm=n`=KS|KFv~OT=tOzV$k&AC{e+9ZZ!3%)@Q1
zSEKb``EB0}4Gl>%UhM?4q(W`RgDxi|gv>{<f{4tSGiTs(tF)q`F*B`^QlQ1b=_C^h
z(#s at 1-iI7-7#c#>14U<^Ygk2*-SowyNs7PVY~@g|DxlzWZHD;#{4bDyvQAD;r(eO<
zkd#JtuUy<P%(}>N@%r*mQLwhQ_IVt%1F!doR)|waK6MM|kS}aHO&F37CvOVaAw4M#
z)B~2FO`b!I2G9 at nCP0WxMn(p%X+ki8Ej1$}33bN#;O{SU`+65V5|W?*(<wzT?*loe
z_%EEDui&h&u71<=I8vP+m<_O79^f4)eZd_b9v%c at x;65SLaIEGKFK>Oh+8?V()1oG
z0_!s2%YeoNvWSVE{v9 at uBpU7fMiT>nia3_5;~sTTdzIHE8FlqgJPMH(48JTpHT4^a
z_GfDowXz|WnbKr;tZ8AcL5D2o#N4+Dl%_>5XM!Q;z;msUHVa})#SQB}PV=69r|&Wf
zajo$B^=mipN)Fk17!4yUtAD*4R#%^wSI=*N(wm<S)tl)hNfYa|H#Vk2!W3B*6bk_!
zhvLR{w at G?JLPL?wqy|^Z#li6nDDtNf<252uQrZ3eeIA$1XUG+dj&HtzG;~_(zh7Kj
z%;U7AkzaSj20Dqyu<kOTIZ~{y+e`Y9qMXCMBO~&$v9VKgbJ7qECcnQnX1JR(- at g4p
zkm?Ag+th-%X9%1S6DVPv_U=?=CI}rc+C7uB%ktnLa}De95NiRqLY$)w6WL_!8`4EZ
zMgN8(D2M>DQ3S8W-1>C0n|I=4NHNqo1OOYQ09rV6T1X at XqqTW}tq9J{WIyo at bvBTf
z#~amm^aK5fDyn_``W1yV%nOr<NHjK!<czPse<DM)^Bd5q0)m3+Q>j!|R#qrJgF#RN
zn1H3rmZc5nUI%N&2swZpPDDh6k(rqcupSMlku~xG!-q05v8w56K_F$2vxeB3ucz=>
z3TC*%orap3mj69=g?qma{}X)%^?``m-w*{qX5?qHABbWQ=nfITukQs!vddq7RDsxj
z^j`sirpRhAKh;Pp9t;@T#0Dan1_!;T0G*cH)FcZz)7=0MJlCVHM~5T_C{jtk>q<zy
z>TfZJ%RR`>&fc5~RzOl7_m)uD$jbvOW+tX_L7sQ|VcsaF6;iw03^X-KA-4%|*4Nfz
z!Mf#O?@_eF%pUAu8NkY)yvJ?Q@@;d|p}g9K5yUrY at C-8sV!>~~%Bh<Si890skc2LX
zd=8n!&^LvJh1Yj>Ox=TT6}s(l?d|QMrD}5i=dZBOpUrnBUL_=~!jN39-u$T)|DXe;
zNCW&=d at c%Pv`7k>0atx`Azvu4(dg=}Tk>jZYRr)wqwVeYM%^}02h^Z at l$1c!`)wY_
z)p!q{x&X at Ix*seTOwG<BXnGKE>(S`T16E!>zIAfaE^&}?sHY7GJ6*Rm$>J9q^rKW;
zGAOLUS)zgSINpe{Y}q~mgKP%(hhEWCd*dhLi|pQGx)W>k`618(YX4$nV)6ijA=mN`
z7uq!`c>BR`M4^klKTy8a)zu*l>=dBPJwHF6Yt%?c=qrJe=&sWe$tV?*SG|1aN_R)c
z{qgZ})PI8Tq8E=*!xa~BF;A)(?E?T6+KKhtDp?!ya&qEXwel4m9eGD%Q*$9)(4&fP
zZ*QM|MgJ&50-Yp_<WMInupW;4p;ds$3oTcE6|kS%obPh;-qJwXGFlN+T2^)e2enKB
zI;?kUQWA%W+QfSNjVuvC6a=nI<#@llC_Xealo%sASne)Jrh*kWH#eUH&`E~bA;5{?
z&_9V at Re<8!#rx5Rq}fQB>pV!TgyW-wU`0k+8ylN}Vn>#1*RHLR-|DK at 0o94tdUbVm
zX|G;1`}OJsdhw*>?930}-5`&C{9-l=gPLD}BtDZ4KwU86;`cutRKO7g)77K}g5uFE
zuqH?1<?`jrbIS#zYLRQfARN&u$TLs`@Y76`l&@ayj-Ts|HgQ43gcgY)RE9{ZAs4?p
zC7(e{*<j5i<D~-Vai*rHB~fn at F!qo;B=6?0gG_etDsE$-r=#m2QV_ZiQX6?|L;^Y$
zFQc$oVY*I7`;MpyS$ISQ(wHwcW>SX^N4A#-k)}?Pk7`<&)5*}QkOyx^&&taBZ+&!}
z(SK>l($2vl;pNeO2Ru;MEKy&H7=3UDI0dxE at ii(c#KdzO^?>1S6RQ4~32~6pC-41p
z>Qx-F>;P7EAIZ^DjM@*)M5oD?CT8?Upw_`Te__>1T8}9Kh?}er=mKr|=xBe7266x)
z4(k0v*Tps53)zDDN1=6ph;W#yuAj!ujKEo2CEah~yJ#U+?$IOE0Bo~EiXz*>>y37?
z!#{BiDRg*rggT93169P#%$WYgzP0slE7_kGeQaM&{dx{#^{g*LXDEq!W at ZL?Ud0zL
z1V&>yFRJP2JOYErKtn^*+1r~0AeoJutrDifoQUxF at bK_FPEm33V-1b4rr9%F^d|@i
zT%G`DRaI53HnUfJ%gVX}QGOl|uMUD4RTPg^>>rfmlKoy<dNU{}$j8U$o|~HxXgSmb
z7M&D<D0A~pR`WT}$;sJYWY2)M1p%8FfS%R(X><Y9#4cXI{5wb?2`Q<`K0S}xAR4J}
ziHTld8zw+oze!A_8y(+DgDFFm%g^KEdjtE=w1lHG3aHF_dw2Jgpit5|)PNUs6WZT_
z8g1e{1v1yjF~Nn|eDN`3XL at GlU&D3{4H8W)Ep(u<B}SB at va*tcgJTT7Pdr at iP6kha
z>Xs7_5uF0oL5=2tlHbI~(~OQYNw~TSPE1Uo&Xf%e(kg0dFeE3z?43V<{#Sxb at KY0$
zI!xFHQWkc0Dby7lK1oLAx4v!{79Os at z4Qkaq`G$x2Q=8IK64`K%m73oB`JAkG?qgM
zH7_YYSP?`ANK-O0>OS1yUTxuSLp at rq_)55W*AFGe$DdMCQbN`Hz$|kBER}8#SfFxq
zkeR<Q&Se=nSkzn|ZY?)2ukt_bJy8|n;9#OrDlXZSyu53^zP_kKsEW!}P+?Ea%zS~5
z^gH4t&3pJl8;yMY{cC|f-gutF7ZVph<wwk32jc#Z21PZ8`Ri5&E{tD5fRwE4MR>CP
z7B>U9b9BdOxKq;8t3g1ZN=T^dI0XA7nh*#<G+6Ej+v7F9L>xRkHTG at 7-aYB+=}U!X
zE-rV`#zz=~NszCVIc*|?rkQPA;g>}ZS5Kib=_EI9Pzed$%*e<9aiKCWIEV}@>JkIC
zYZqv942x~UXvl(WxMfqeg|;=Wg3O(O*;9|mUIuxN#^B$Y?N!idjp#rVn7pe1g6`WQ
zzd44ff*Z3;+is!Fro6nosN?I1^CWJ6fB!0ZgeDcWgrwv+K&+Iu_RW*0PN74>AIJs2
zVB1|(!Eu2jLfeSZF?)E()t#MsKy^-#U>Z-JtdUd4zkToLhZ?ScL`02aCEniPHhBsI
zjf$~>%-qEb3i6zpF+p{V(aA;B7};f>u5@?8kAsVALXam9by@*=G5+iIDRdeuIp*bi
zkP_?UUz+w7we#;$q80>iJUuyGw=FM&biiJ at aV8DIX1v-PHRA!WJBfo<%~AdZ(?2qB
z?-0o-D!Ku1(+>;+laFddCO!-`sC;v#y{~UpP-un#ZGVLEc<bdg4%3eA6fO0hK7AU-
zqciB?r|n at tM{mFgB1jTOM#fjEsZ1tq(f&_zOe#l5DL|Tpnyy{O2VO(nX0BcHf=C29
zu4-sFpJUQ0_WU`=n>TMX&MBRcm6b*9QOaC*;!;x11DGHfK#eT4w6w~r4OhgegD9Dp
zE*xwxR|EYi>gfeE1~Z_}^Qg%y0Lw0(QU)k7f3y!5J|u2nKz$h(m(ylMNNB5Dd39?`
zHA2R0<`^OdReeUEI)``jo{kPVd`d}6>k<P41M0GI;=~E`*}<A=&|f!2+(^);AT{6t
zV8T)9`O|04RMynI#a;=&m6DcLg_@|3{W?)oQ*)1&5EW2%UT<U|xOsCZd5}lqZA*(>
zO<f%|8yg;Q#4}6FxBmXPfYIYvEWUz*0)*8)$}6knZ1Hb=&ezt~qOMVdgoIa!iBXM+
zc#prwa4%jJ6{1OU;U|fSiPh?hDS3FVvX`!&)YR16#q%0RTlrA!N_2V~GSv%YY4po|
zv)h`v=MC#Vj0={Eure^5t2o|gf#g+ER`z64P8utkjTV=dmX4Xf1k64;)fmhVLI(kG
zx_V|+OUq at Lm?}uz at yW?*`n7#VYB7C%Ds)>Ax?y1?xw*N&T3d0^E?Y?J at tK(<wdL2S
zqy^j=7z;*rc5hH6YTq>~=~GDFDOp&GWMhj)-KcRkv7;1_x2T7=o0}VlNefBdsCL?q
zAGi=XfZtLu%AoEce%C;EppqB2^|cy73r+w`^cUDrpcWadUv*2JIZaw4&H!zfi at RB3
zR)#9U(yndw=~biM%$Sw8-rna%^_jZ=<e08Cv1C$ma}%Ln!F)C&-?!}x0%!yU$!sc)
z_~gIxzYYw%VmVU!g!PWy3OYPm`uF$%z5RfQ%!15LJg~I1vdVVbv-_ at B5sg&@?LmiH
zyTH?l{5`Zs?@U8FJ3HU#z1%ya=eBkRBACPU_s!DvCYIHW4dwK;XOb3XW|g42{&mI%
zYl(^rkGL$dqQb%0_9++)ypE0ze!Hpj=x!Dk%uxwn2qDx)A|L>t?9^gBs%8Q537OTw
zZ_cY at l>pwT3^&2mt2Hy;`+*S=q-an7-{YN%j{bfh<K{2=tr5JIo*VVFA+jjnmIn)H
zApESYt(_<R at 2tYmrXKC)P<QwBq0)$mn*sX)yg+gm4N<wey7s<&%q{Wu&6|@5#{f4_
zYjuDk7!=fW0`fZBm3ZTh#9LG%6sFu8k6cVj>Kshle~XcA&-3mz%?#vO at H?+k0>gRQ
z&;G6iP6U|cK at FQNEG){al{M`YI?D$EY=8?Yb>5yidE&nvtY5vtadvgh5gwAS{hz%6
zzPGQ at 1~&!<2Yc at BI-_*r<RpN3e+{{WpaylU9~>kD$aV7`FilKOMjMX%D?CKeQ42CM
zGE`6(m2#}8 at Q8V``r|`ddb;iAoDzDx`T&ahC_&8F%F0K&hhLro?z!`&=KTCgSn>B*
zu&TN`=*yS0V2b3zxxC1pKkn%|+x-ai66)Rtxc~zYo0 at t76=2PMMiA|`F7wRHjEa*p
zNN|luRFpz2_6i#toA$}%n3x#N==1&E-ETqAP5^eJbOSqrG62Slnu7!FsBiD>oe`9q
z5xJ`{?dR)T=}W{A7#Vp9F3!g33dH}tY#$ww0As2h$w5bVrnR*ds1wDykgzc7l7f$q
z9~Kd at 4p{OQl<h|tbShR>T-3lGvvPA`Vc}bP`Xz9ND>Y6S42F+?<P=06DzFdk?T_P!
z6RV^7+RvUbI$=lrCngL~7x~{`1;V&Z@#y8ke(kT?(TB(eef^61w^313pQNLs%gWBK
z1Z9e9YlTHdZje)B*Vot4u*}Up`zqp2y1cxMx?Z5ZoaY2}?g`7#5*is9jR7M{%gTy@
zV1Q}hd_HzhF6wT`!y)y8gd at n^v9U@|q-8#R`t;P?{5=pHDT~^7R77)hobz%u<JCw7
z6@`EZ1 at IRCsicb9Z2gWFM%~@#=I1M`t51!LjI6G&R~6b$cJFV7!Jpq0a=rrQwmwxk
zneytz@!45#M7xk>-*vvx0==PFK(r2b2EH{+=mpnxR?W8%R4L07IS3U1TczdX5ZIxE
z0C at _%A^_6>Cm&K$7<Ug23}=5wJ$G=J61*LP$Q>LTv4DU8N_BK#1Mqg0{5rdVfkBql
zARjvA4(wlBalFMY7s=<3&01*&CZurRB+zvpKZ778iP+(zaB^Z|WoxTH_BqJ|x<(_e
zn|B?~qy2-AA3sK2L#CTUbN0SkQ&Q8?(dmPl=YKwi12Q)Ygw}VRlKeJ$@+KoLG}x%Q
zJj5Co7Z>G~^J7a(m-Y1YcK7$I>g$u+GAL9rs;a7JG9EuBM19lYS^piS=K#fG?P=h&
z at NBeqXy`+GyCSNkQ|P=#jf%1w)Ly);TS{{393IH)kf6{|Z=Dh+eaJW at P+<(Sot8D7
z_LFbs<>ym_$N>OBkP0D*XJ}}MhO09{2BeYcIlQzK5R*WSA6<t^Ujz?22+-1=MzX|W
zGMvrA(sJt4tI3H8B>7<spGP|9IAu*38ylmtU+5R%p}u8i27tBuSI=T=%Q(`?;k8(B
zE*6#PrKP90BcA*E^`?&mbzw=#XmAau-ITOQmEo<pq@;HbBT!#DkWSARds#t<+g>s{
zdGh2_ef>&IN{gODwDXN?*J^e69UUEuBxE3I;1|S|m4mU0J!)1~te=8|i)wm++2<A(
z>@G9_wA5$mSMTB}r>dx`+FsgFh!*l47|`Uud)Lmfx4!;dwKx8(V2emBSRr)oja<az
zSm-R?O`l)C?tzvNUeBEa5T~W3eJ(S2=FFKZ7rmZ4I+ih*X${MtF|)Bj+8VI~FfUB$
zJwWQoI{(S**Iiz`$bQWo-QC+u&C7c&H8oXQUcL?<seNPw at 6Me&04xSTp^yp&WD%RM
zB&hG}>W&C6*KN$Up<YD0tK<I~!jY4svh3&1o$DVQ%xNnMcxGWCe*gZ7ix)2j!pt5X
zx=l?@je+n3yOC8;FuU&wXom{U$owgz6c#4Gb?X)*8=HyXA5M?p;NWVyOQy!gwk3Aw
zPM?0dKGkUXT85p8g{2xY4 at nvThVJg}Up+n7sHmvG_vj~wi%KXf6M=xNXH@~3ku)@<
zLB+;F85I^5B9Vb|r9D=B4=;57Y^uupswy!fV`C~Bnp01nJdsvVkb-Z&!>7Q5um;XR
zZFxX9nir5HCMKQ%S2UCw4$c9*9IE!3^eD29ujMJpyAyyR#sPvcjKPhHii)p3f4-WK
zkT3?>jUwFVe13jD!jHEXh&c4CPUl%N;QW2m|Iyd?zJmh~0RQvl0bUIaji(?=hHhTa
zYDpsT^72BbTOf(xFGWqwi`?AY*49Q%pP9 at f!`wvyX8Uu^hyYDJVH^yAoKbs?fn2 at B
z$4sDT-q at 7yon=snu7L}poeYVOQdFHJYNAve7rL3i at 11QE-qDWHP<aWFiu#__Q+P<o
z%j1C{eB-#-i&T<q*|$ev%zy)&7gCc!6zYfRuCCQn81AeLGcq$zfYDJxg}l#Sx>Ox4
z?5e1$>ILLtfZ$!&mG3qy>mHs$YJ(9MDqIDM0O?&BnG2w)yn&s!ee<7Udiwe%A+v|h
z=FiX1Lxgw&@d^nEp at PnZ#l=-Hq3_<kbMtO?5Qprzga0Kw+;?-+LA%`T2B_54)zz_?
zDxiV=?*5ZqjuLMnUubUMz6_8oWnsYr)A9~vSWC39)-qrI3NAn=%5J<{LO$QW-$r@}
z{JN#*wdku?uTlb@*xK5HbRC;P4NnRMZb78yX$fX50QsW|Ai#R#Lq+z|GBTBU7JaB|
zh;{2<<ic%i+At{}NDCbouYV*Ld<Q8xxw|&`?Afz1zSU at cTWU9V_l^n=kMe32bTUTU
zT0u_ka)xf%MGz}+Yh<WvXJ#gQGwTxl$fw-DY5DorFI>2QSfR*%HZUZ_>&utxB&4Kv
zcSe6p;DO}Gm)5(_%)(-OeE4E&dYTF{1xP|1h7|<v1Ao%mWo?QpSAJdpmGbSk@>jq`
zWJx+Z9|MKyAMUQ98pvlc{cN<tG$FFKYZH>Fa3TaKJovA at joG3E6$p65n^7hsxQ|n>
zLh_ at t!I%orAFqpw1P%`mIbI$*qxOF-VVvzfJ=22PA-$ju0pU^p0A2tPA)yYs4&(zW
z|AhM1KyC}`I at oW_Jj&1~rl1G_4G8e*?CktJIdOk?_nwqga;S&k%bxD;F|Y?<Hzdu?
zQEw>9;Krw>ru9*$xk1tJ^Ydrj_tr(11Nbm9HJyYBK{O3iO9lK3aARX*gF3k=C&}pt
z-?!@oVFX~BkeEmXRs{kTT;x573<+uiOH0eKsVUUj3ALTAt2 at ib$M<ht#v$n(@_NW5
z6&8|6N=i1gwMo5vDFOzYDoFZUe7t8zyi|2Z2LV!GgTYbn=qbQnkqFq)qSk5<x8>FP
zpEb<Pn8325YO8PGo<^1WdwY9P6)gyc@%i}+U_29brPD6^oQDgb^P*rW4GayH9zQ<m
z?(UB2DMR+59D4A91c-xK1{^Awi)4kEIusQUjt at iG?$ZZ at TRaU?pG45<HtNG_YU-^N
z{~&wVCFI_tN4RX-g|99Vvr)pM1Ew`JHr}_l=XP*(<b<Jw3ACGhym0saNJj^9?*^mg
z?)(mOS0W-K(Uc(?hJZJACwQkHCiQf~w at F56!`EZ at IVT9eM~@!OjaG=FG)9dAnwwG8
zYx4pPPoKc3DF4a&0Q<<)-oC!-{{Aqn20 at g`g at sw!x%DdQ>fTFBmZ%9RnWU<^y11Sm
zg@}j<@GI(4HTdk(F8~j8QWQxU6B85X<GooV{6mIY$`TSz>+9>Io`MJ^f!oHxm0@!j
zM4QE(ocPf87Hg$F0Ft%ME(ODZTr+cSwjr2&xEX|1KJVYZfq1-rTjNzvx_WhOtrvFn
zRw`H|02dVAV88e9y!FHC!3&^XJg8JXfG7RvIL8OgAlXFzmxuhvyOa3HwD<J%AbJCL
z(E1~J!Ef<|ApYu{Tvp+W4SJXY<YPeY|N8y=Ble2w1BkUdZT;axT54)%ak6rAr)H{_
zmY2uJ#&B3zSorYjaz1{<(=M_*Gusv;1>6V`6a*R1qMm_+bNlw~v4w?;ps06ucTtlj
z06Zb7)|3dqgTuYqnB9#wQQN~^TOMBCN|*wmBBU?1wY9rVh{{#~eozyvj})S4eeUy@
zFIAnLgov)Po}K`B+r<moqX?IK2U%NFTT2C^AJjY}3ybHwcW2QJg6M|}qkdF1J_P0^
zCingzF#yg3-KeG}DR|T8<5g#*6CZyd;XDbd0ridMH0z}07Z6CDPAdAa{tV;=1U0G?
z4(Sfle*W}HbUgp~_?SfWr4Y!y{)iImg}FHdJ75)G*VIUWvWm;iy?S)8;|+*-cyz>R
zKO>jFR%Q4&f_AxLLFh%MJ!T8xF!<d-Y1%7(wa5mb`-Myfdx;MEe);m{dv)h2Am@;f
zkV1!fbb_RiYzZ!6d$4VV^9v;2_{s_%=m2LImnu|S>+Iz*0lTCERTULVDykEJUP!m<
z>r?rhuYQMHV#*)_5=KK)bGz~5kS7dwY)XocmeE(ZV;DRhK0f47S5{Vr*x10pKlKeQ
zONfs at 1!Oife+Ct|&B%ZlI}2uid}0Evv!F3>`u~`E?|82J?|=MNNg6~$MF?e<k)((Q
zAxaq`TZ(L%*$N>cMK)R4D|>|O2$7Y&GqTFc{GG$~{`|iFxNg_grPu5Ed_2y%pZonh
z&N&ZD%ik4Kd*b8c`=vPQ8X82wgkQaS1z3k!R- at -dxw(9>K*KqV4PMlI<kZx4XAISg
zo>6pmcFxYtUDwb!h4~^(a!hvfUbxT?Vg<s|bwEs{0tE&9B(68`&PPCK6HCjNnVCGW
zM&LeQEYH}ufW`Uw`C-bSroR5Bni?(N*f{{%Cr{jQj91*$Yid5JDN4h{LqLk&zCAQN
zI%+!gy{PC4##|a28Ulflkr6c9(_3APV%{#Xv1h*3*K6wO)iMP?WAGI>Szod63kX2y
z1R)0mZj?4VJDWhrP&@$f4teW3pN at _Wd_cxx%ubv^&cwvz?&DKk;c*yTP?`c_4lEu7
zfRT%Vx_Wvqii<Y{<0$zB1j>P-O?B$3o&j#9q=ffM#>B-9!K>HQ)LgT-7o4A;M^<$^
z_`+Ub`Y%F4s0$ar-U8a!9yfl)H$Od{9AqOf;Pmu#6n&yTwDo6<l$Deoj(v}`&zY_j
z249tslzci;dldKpo;m<D<9LY_<y-<`Wo1Q0{qtWUa{&Q?><uIF(}(bC%FT`S<sqM|
z_mq|KvTiM~OXNu%#2yFAxqP12GqZW0Z(x=<eJJ#*Bkk9(Uy+9b<(80;_z)TSoLj4^
zw7oq*z)QSmIv4EK+S(fL>ozq#4=Uj4!o+>>7+mPRU3fKnZf><Z1*2l&66d4yCu(bJ
zfnS!Md90(YZR#0U|Lt4<&`|jqZ2^A%PmQyf at 0?v<>cgxV$S}N{b?x8Zxr)PgORK70
z0`euo?%*I9%<cj4FNGk|c;)W_JXiYkbu<xl(e6D5X)C6C3Mr_m_xp<DJ(hSUH4BUQ
zh|nsa(<y#_DopSA__)cjRStiBhcJBwzJV>9-oo1YGDNNCz{SeDx<Wu_zO=p#QcbbI
zoRy7 at twU7k0Hau#o41IF2#QFSvu9lZnyY;14mvtIV(f-^rkAfa3(5d<mV-k>zHi<*
zLA-yWrw8hC4}cg68JRZo>!m1ALL8cfe=%wX-6n>x)X~vlGNc|Y=2L~2V1mI*{hWFX
zfX%|nI#5*uE{33W<k+!ja8aOj-5syNOme4#4V&xcft+7NECik^=6&+|&6~T44;&|4
zy?Pa*F(&q56ZG<ftSu~fF)09ORZ(5d$j-iBgh)X_VcO*>SaosvqXO(JS^74I7s!T~
z9p^V^IeGFVkV(W4W#vp;<%2y%)`!70x(6h}+@@7Z9B4`%+s6wE3(fS)kS_rMghy+y
z6bM*NpW^1GKq at FMj#Cs)AKHtT%i`V3g at u@p^!D~XTRr6n5nEbLu3{uRHZG2lkMCGY
zN=o<3io?uO!z+mfqz*IEou{W(QS1SvZELUUp5y!iK{q1F0pFanvw4$KQZyew-VG56
zU=EYCRaI5?Ie)qfEcUmjsfz)X;ZQGrjf4oIZ~&)zl|Q?}_qH}-0^WX$GOxeCbbS*3
zL9DkLkIyY25JLU^Yi;eIWo1)ya~bS%zH2?~%FC3LbD*hGE%&SPXXoZdfBJMtAys97
zSRQufo}wZ|>3!rBD@!v60Vj>(ZosPoNniM>e~_-Wt}b6>sNp6rFld64!}`jX?JFJd
z=IP(Re`{!I?E-0(ost`tn3#yu-Bk+AabidJ&|sw}7|zbwKGll?#|$mnwBYx89~Rpz
zut|ZI#_Ok<n20f^UwOvGbT|Cl8N(#~L3^-aQ!_KX40~N!MFns4^%W=DzkmPhla~(x
z><vp%U*zLMhg|u_`lr6W9zy5PTVYRl4HcEwp2s+&l9LY{@*n~mLt+>j7KZ2#76Rch
zqh~?RMUHJ`qd3f`!P4OAQ*I??<sAh0RW5FBy!2O7Tbmdj5(BHS8<}fR(217^PV59G
z|K7dt4k;-q4jMP=E%HcGNE-wv!6>p(SX6WpT>Z`0M+AbQp&^GyZ&lT3c#Msinc2nJ
zNFtyPACi(hL7Hf_B|orpE01UK5P1O{+(_p1_NWR7r{914a0Xxmdj&~z5Qs4tEP}2f
z(>njVyPlq&`H%eIuc+ji?0`20)ox;HI{3C- at XD2b*!$GySKf-B21m=+c`kycwtv60
zy86NXk`Rx;kP!Hs7q*!B13JOQ%j at dl;URJE<NI9jV7dGE2RgI$S<amsIrJ$_CI1D8
z6{A071Pm+s`kp|x%{-q&{Aw-U>`4G*VwMUmjTCk>S)SEI3)QN6{Uz$AC!YQKOY(yT
zmp)@4l<}*8KZ1qQQ&UR|Z#};xAi&;g_wfGxqq!&M#(%`&P+{0oAmK=@K-%L7!knB@
zffh9dRaLLouMZd*od&uMydG8*v!|n?(;MyG-6tVfk&_VFxp5v7aY;;M^Yi!b;kjxG
zGU~|Dqmd~oq~KJbBbE$K4-XEOQLZdF$+6u88b)^VlGD|V<z28vKmsfLSY^4a|2%qT
zYx|dfhG at -dXb{uz{1*GTxw&B-ToV#laq=T at mU0LXXNK4nRaI$#NnuBlh?rP3NJQ95
z44DH%L(a&-8yci^FLF(fja`GKmywZq1ELRvhstN0qr3Kyi2%J0c#@iy20~R(1{@6*
z6`?{(N(yX&>zfu9baeOhWN1~B6$@(y3XQBROm^?U(_4h+dn|aTFp$w&Lp42P<Fxa)
zIKj3bKYm<OS0}8k&2;|!`8Pm^sQHXAg+xnBi|5n$a3dqFI&B=R&VZ~&(@E*+Z$kF7
zv#|}fwPHTCtZXN6wWV)vK_MYp5Q70s+?%s<|FwZ23E?vyM4RB`{54v}|EnD0a(?!E
zetv%RcfQTm{P&$eunoz-&PD+w0Y-KU#)Ahi;KUcQZtWdmAvy=CF1XK_6pApt!{pfR
z-MhP)j-EJi0=&fu_?U{SYW`^+kh=a;o;;f~{a>6FKilwDJmki9A`qq!u*179<U~g9
zAmF8bV7Wa!jar6=q~Mqb=!8z%*w|dzWLg7c8EkuI*)Z2F*l4!&YpLCiTTHB4DD%Cu
zv?1UyN-i54{&r5hv`>WLIWTtwD;=FZKvp at b>%pM5vKzfg5Yqv;?#alME$nWHxatFL
z2e$Rxed|WSgPlR&jkL6)bIe}UUW<L3MF)CzcL0a#pb>2t-&^2lK<`bNXZO0^I8IHy
zwUiKcUXoH*oy$3S;R!i0DV59fU3(t(mp(s!;mI2r&Qd?|)A1=OHw_KXVNb at oGT3}=
zt%ORUC24!As_^5-XW&`#8ZHt7gM&9xu0Mp=rlzJoe&WR3vNCt#hy45?>WcA`-|@P0
z at bdi1%fL%<awd3ez8k<5j+*}Byr;aTh6MaY!i=gAya4oQVp0+w6N2A>5{!mjIePRc
z?8h}--Elz=ac5%Ef?x9~4V6{$^700QYI|GT+D!K at gzY!xKg{3N)wN^I@$=O04;D<z
zC#b36v&X{SK3%x&PQuH}yL82<JMTHXLh6PkQ}epT8~`p&Kzi?k0DsLLJ<jA5pE5I1
ziX+;yvhIbQNzcqI-=6N`4)~BQRc2shR8{GDEMH`}!I4w7zy&POG>wOgtAAoHU9((6
zTl*zoj_HEc_Yc>*OKRV9wO9)lTTD{o`l*Jbsh4;|>@p%0K}55#u;^|iJ#n50!jsD2
z3IsnyA#9Yju=O6l%uo;~gl-A$6Q7(c0*j4g?*9GV339dTufbDZzkZz;V+bT0uS$YQ
zj7KrgpYJ$u;J}BpG{;8a)=8|uo|v4Rv2JN?jRX-31RK?7JMqNr+SG)E{J72UK_pin
zR*+yFNJqM+%4);6Z?6TKR31$Gd~*g;%>cv at g4C$M5mB$KtSpmToZufSgQgJa`Q1Wq
z-?_7Sallwl?-gz$a6@>AqPuTU`Xnaq<1^~M1{^3=wXkYow*Xz(*SMX}3E;@x9G|T=
zlO~qDiiZ(AItJ9~Mn*<d)G3x{zBYz=%7Hz;q!)f^V$t#a`>gT7`<F?b$2L0(XYyE5
z7aaTPY>p90OOuP-y!Pd##2&Anc<IA;qN)OqJ4#1$Gi7v7)}Q;RcsP1Ak&nCnePDOT
zQGb at 5F9ca$9DT#&E<SVC*1>UD!0dtI(VH~2zt(N0BtwIGtckU)bi&QT&4?h7>k9&x
zbxTe at i<y{Q<G%L-(eQ6#LsVZN*bIGE{cPrwnQ8W}_-bJUh`Qf>C3J;WGos?+B(OLA
zQ&V2Y)Sm6os`fUV%_KQQ!E0e~*Y5lj4lk={j+Mk3bG%%Q&S9DU<DHN;jXgSSpc=lu
zW2QK9!UONN6||*<-3F5rzIM$?B at fhf)9EfN;9|p3!$79{VZBTRJm!}!Ubx`*SCEF^
z9B=dm_}e2OP^dJ;NIGH_BcC*4=?l*^2=DP{K_8SJXawKj;8*kW=Knf-D=K2zt`#?z
zc#TjMb4w?+q9`G!qI%WmxJiSx6X(v|47q4HOoj~vf3Er5E%0LF<h)C}#>2rOHJuDX
zdAF>rEM}d+yzz#5o5kNhH5#TyrDA1JYh#JkvuBqW7#V>P?GhFi1_larj&`i~vp|9Q
z_ at 9;;!Ts<y3?LniFp-M4PuSUA0k)6C3 at CIy<FlFG&(tb~T|@Y2-wUmbyNj$dZ;?H@
z(GrS*Z^w=ud6rXDSO!Pt7XP5`+c&>7u~;zxPp7M^%i`;*c;E7WrwKoQf&>@{q7;yv
z(rPZ90`x~ll&bdm6_9c{Ik{`<>U2g%MpQKGwhI%jAIuo*{pC6oGiwxUoqLMym8bXb
zCm42SU7u`A8QFMi*p+j$&~i!xurSy0{$_s05&D3D01S?oFJI2?EQ8oKyr`K#lWaqG
zMO9U`qpM5baL>EWB5TfDqz at z{UR7B&y6!(VY}Q>>^|9 at mck`0Z=g;dr>nZ`gKCG;)
zAWgrI9kT^ZZ2hK^L$#nL^M_>L_4*9Kf`r<yU%!BOMeEU8nIBio*7I-Uf9L0S#4BoO
z;;Lk(@h&G<*WIYwb#&Yor at HE<1Z7se=Z%x(Q>e`QtO<XX<QFW%w^cBJ#V%c4T`cKp
zZf+iCFz9dGIH_7--qhLfLylQIdQ at _vMA80__ww>HSaw;XO~a<B-P`9g at lB2y_mkvJ
z>9~}YyNJ1;VYkC{cRu5iR*~&eYWLz9LHxu;PDx40yn=#S-s`ZkCRSEDh9NtHMSK^X
zfxUgb<^1j2HzgXfZ5|LQJ;n$;rfCfHm*x(Lmk^Zdc2fh0{Bhq0aWk8P__dc8FNa(b
z1iX9qK4jd{)lKLoQInH%`SZtg_}4E`86w5k&$6=%<Nxrz5j+*g59Zm?*;yNQGg1AX
zcwO+110nMUw{C^Oa#?H3CWy<e9E!J|rlz8*z1cN7I?Bbv;|xM!O%1;N9j6@#L*7FI
z&8v51;OnH_?<*>j_)EA#xB$|oedC_^|7rp7+7qCBEF2u7IBD<iU(i*jt$O376cmo7
zDCfR(c6L at V0FK3CB{klWp%s1N=4;yI#6*edc-hY^>iot$=g*tW^a^_Y8Uv~n<_qBv
zrxgARg$Q-C?)|xuzxTz&;@av%z~59Vm|Wr)*53sL?4c(DaRU<e1t1SfQy?_1uC6RB
zEN>cnV0*nDvCiuJ5m_k;*f*v}7X1APKM61cdIp9Y;g1DCy1<IgE-qF9gTWK;`Khkl
zrqeEy>{qX<Z*=>=l1thQ4G&LU=)5j*UQto8rv2i_j~~-NeQI7lvHX{(P<u|UUOG0B
z`Lfeh<3lmjPVfl8B`^nPZEX!){`#$3djY3Vn*sQ=*$B=4vK1|O at 5{eWe)DHjQ^uDA
zE7wUqC1T$i%glh$IR4Uk{P?TuSD9ZxZB$ZbDEb at CN^F{P=GC~)pMQ~%kYIP`A&46Q
z5PX(Q!xP7kKbh)(@V9{2X)Rw}R$AIO^z^+1IpCZkAirOEdXCr<jTc|HX}r%_ld7g6
z9~K*X2E;1wzBc7tBd at j;<+^qqaAjZWzTh&eJ?sDSH^<{52C*=h|A-zj{QakYN9&as
zJ^Mf>z_$Y(YwGB2X*mK;@4u&ki;vF at Tnx5pq)|gvwMS{`Me9|o*v4BP^Ospi9^(GP
zdq+hv{43^SX5M2p-JP&-!T`t+fKZCpbeMpZd{KO8I7hY;Dh5nOHG~P&eETLMDoXt6
z)2HFb5NN7C+b-P$Oq%=$M9-A at C>Z3{HXLc at b%F1OpnhTcM7CF_l39>BjY-L48CsD7
zA*9v73xHekntC-yGttn{fE26S)g^v&yj5UC+`9VX#oFIP5_j%Yh6~!JZ<o+r2O|Zl
zUjc^6Z8QJG`{Esu7V&h!xu&ke at RMOWB7wJK<As#AGoRgpcyul<dq0H;eaUt)`O1$)
z2v@!l5eLD|G2O9UT_=W^ZTBmA<z7<jpHpeg!Yd^$LNb}_w?P|5+l%P^;Nj)<@0yP(
z`dQv7)}m*T@;B3FRs8AG4ACw%*h~1QrR7CEOS3l(`U!VpYy=`&)XExIYVp{X&T%cW
z>GCqjSJUZCz#kKH^OhQg9KF_kz!*UXKTl0fy+SSH;2_kZm)$*j&z@@<6APCwpZNLn
zCt;)L>SSkjQn$4N8-s5AF3C4cum}3>saW|4(1Tguhgw<?9@!j;J32aQc2z0RC3gUm
zIj5B66dq1HzpyYe!<<0(d|p)<#FGAOe^%Cod at n^Yu{{uduPsgY42+L^z}l9Tm+yin
zxntVjTvH=XL_~xK(^H+<viv6*65Woe+ffh`6T^zTHAj;BQa*U_z}?3uVE^}+y<!la
zM?66E%|yr86<wGuy0Bc-(r{|wR=h*K3QyXbkP!AI*}D3A|2>wCb#?DN?ewN|&3-jd
z?Y86K;4rbUc)U4;mtE`xREHQ6&-)~;g#*t%ZI`BXL#YoXM8srg^8!=U06STV;|5EP
zjEt;0f6bPT%CIhR>0MLK?Fs64 at GZHXx9a}<`EyTRo+;N^chXJEzyK4m`Q)lp$r(1b
zfqu+97cL~X&jiIs-nx4?fVuE3 at gRg#(Uj}o at TL#PO+_UoJ-a=(W}c3JUXSm)+Dya<
z%Ok9$6kJsr3CnpcPAcpIG1qu=EZ)0DW!D=-+XrG}woTa>EfSkEfcY&droDUj&KbxG
zo&@vo$Zmuu_4d9C3sapG5LHl6;OKlmu at j=gh=Zi4sOXcXDA(?*N1Q^<5;v%LbxZ?H
z1K&>A2geA}0!?LsxF9V}ff*xsr}n+#H-GX at 5*;8V1(q$Fq*~qEOAGGpx_tr3%b1wc
z$*P5v at 0jk`B=<2(%ieqNzytm)Km7q!h4_l2wzhVPe$V at J%29VhE}9y;wm3?!*uCl9
zl-cCGRiD!vykICptq4(QZ6l!ba|{(<g1RmIm*{PH at i%)8CaQIf0w>YY(E&~(7~6MB
zFp)_;MpXe+K2@}5%UXU+OiWvVI$$W!$YHV*o&2}tIi=pV{4P~ozj0qL at BOQ2cxOz#
zX=!QY-%A8Ppkkbt-26oEOH}}|FkchkLRQPgcDye3!v_MqylLG21m~A6ifjo%?g<V9
z``6NzZ|Rw2XJz^AFNx(8Gcz;udJ!{0x^XAtx<9RtL~QdVh1eS<5JmFyg!y$OB_xKW
zgh6y=zY at th`dDGck7zBeMr6h44L@&CaB%JJ0f?P{4APtP*#%>E`j3tx**x2Y*-#*2
zw{PG6o~C{ktIAHDA`}-F*B1nS{Pam?T7$vjqObNhf8xwH{1<NRZas76%%5K-3sN7P
z7QLf*4<Bqbz7(TR4Iq?d at Kc6kXMmB0fq?<uUE~#1Y-nIWRpBnzVkchNxnz6f$dQ`L
zmS)=<6C~$UL*z;_QvVhEwiNv%&a(PWe4$UQ`JxP?zxg{(Lq^rjEU=Qc*Zr+ at XXEvX
zLqkKk^xMv!RZPD&e$V}$v^0HVOGr>q0=J2%np(IhcebCOTvBWH^zI}+y%vM{H$t4T
zBduw1=gyu*2V$0webJI**j3in_MUWFP6y(1wrF1ft&CI#EIX{1=`LlEMi>YmJYZEf
z)-Nn>%emdfE4+YsUCXYdq*PW~N&utyUc)ONG4)~P0Lh(v^6^`*JUqS>+pi)*W7Z5-
z_xMGF<9NS;FQb_0DOQFkrq@#**^eJR>i6U<F0JySj!aAQ`I)Js1r#hjJKH{L)mo`V
zj~Bpf<b*P?ysaawYt`N~O_ at 4(|1|-P213eyaxPQ${{1t#ZsL_`GV}%jz1<&~No!b1
z`B!5)#bT~MjeYJDlOw2Way{YBT^o}(U#i4nW#}(oeh at y#5C5EYIh|d1>tFlCk{GYR
z^;^GQv`n*;rnlvr{bJ&I#;prVAz%;DZ3&4RN=j51*A~+Ng}yoy at 24LwBPC!Ieg6FU
zl|UB{507r9GuiiV?OM9!yvQ4HJ#t6?_ekO6+z*KWf%Ggmn4+qNz6J4+kdRz6FgUBC
zqT)y9=1wcUa3Uo(Dk|8u6*!0K^!15S^>v}-(b5MXD!>2u at vf^X=7uplJ3H{dDAhvC
zSM~jONF&S})jzK%t!&oS*1m~|;9QFHWul8OKJdNQO|<J%@NN#?fLr!jF&>mLJBDK(
zH*0wC>TJHJy;9<l^^W+7IN+S8a<=`ZT!bQ#Q<Y$D&I}!fe`hbn2M at kfRf&cti>=T$
zUp%poR~39N at T#K_R^(wxI6!}6jJlSlW at L8`pjS#$8)IuK(6O%;=OC1t(lI5J$kgwc
z%g$bHNj6N_*m$$T^FUFtw4)=ht-l1ohH8qxxxBNpKx!8yP(3Z6hj?8po;VwH=lR|Y
z6<LBHoW`w{-Qpvyx9c5Kw5PZCOD``{7jg)}rd_Tv+RABuZtuknHJW2IWYk4<KmDLQ
zr&Kd_QbyUWg|bSisml3gr-+D1%P{c#!xR)0So<s at LUicRAuMPUvR^p`z6;xhRdSIG
z6coJd;o;$Uoc*3dHPM^}{;Jn2iP$nchqszh1AVgR>({SO+EZyny{KHdwW>5qopu-;
zp=XHF<^+i}*`8JnI#pUp2`{@JT3_mO1CHDrDfkJHzM`Vy6!23#Fur~Jz}vTP32T>?
z%^Jf6o01eb^PhE9R=VE&0FQclZL!N3kAxEw6F(#+C7E9mnGby7>^v|q at Eiy2(d?tZ
zAL(<L_-#p$#cv~}zNpci_f*2I<#gDkr#tbe9!zYu{NPoM;Tm5FH<1<=!=Ima8yC$H
zgYOJXOeC6LYH`XC4*?|`Tm%o7|3u)*m6LOGb2x-A*Qkg5$dRo>E+Qh67%m0|y=E+=
z9~p7w)o)|L2atAF0516E4LL5!o`bX^Mn)V37LzY=NxlVgk9FmyReke;<(rQi9DImf
zLzqtlZ^1n8S(Uu>rpA8ImCVPCBz-`IVJi>tP+D5rv8L$%RoULXd)MoM7Ca%9^Eu at l
zjjGot{=9x7(z5)!r|_8U$J<}LsQC~zW6Od3!_Qj?GXvPjf(4(iX$7f3d=J~#$;zv!
zyteAK-t)h%mb1z^x3w(Q8En!)@#N*_(}M-{*)N4P58T^T&0q+hq-TlQ5g<5NcL7W7
z?&0xVBK8KTc=F at NSsHVCGPJ~Eh0icB*gSlBkj~q3s#6nqwS?OZ*u7I&6TP{y-tpO%
z&ztTlnZLwt(EIo}*h#=|F%e_#j#ecKgg(cp=hfr7_w at AiJBF%#ss}4Q(G+5ZZ6AUH
zqM)Ys#1uJRZV2cAV%epr=nCPQ8yQXizySR*6WeFcJ^;u2-rlZo$rPS?>Y_o1laJ5}
ziPcQ+ef}d1HhADaJ3n7B{Po>jd&$QAaTE8gpGV{4<9H2bRCIKPUMm}xgN?rl_6J4x
zaP(Uc<}h-M`x-XpL at cs^_!O;97v?#v^MgG0Cv&5^M5zmYB%O)dA0N+RoioLw)9|6F
z=;Y$!BGt_<P{X0DUa(~;;4(K-<c9*(9i-u57@~+7fs6uBR>In1tyW-gFrGAH!Gfsw
zNe}rHB~}`6K<Vg@@<}){{Nc|}k1;pGWzfNe)i>az5^jqvKp`-rl97>NI=7gu-~K at +
z0gvj9dJ3>0d3t)<#LUdOuuy>C@;8Z~pkS;pGx(+vd=?f4g46ktxw_M0^W*FK5I#mB
zAzIAH;?yblAtGF|nP<TX+rS9d=6~EB`<WTnOwTK+kfv5y$64|k90eNyb`c-A$A3DC
zI0bZ}YJoZ4GnHY~!yj87DeANZHzK@%fddHb+i(NS6+{map0E7-hr`R^Wzf(C8t{d=
z!6opjIIx;68;S``Ok{%((ZJIBOr3*=4j*p%c!wHo2v2f9TzA&MNiKo#D|fufcY^sS
z<`|p+YL4~Rm7d46D%>gRA8LGi^=e;5W#!k(E->7()+7brw{Le6P<SRKu at _iQ?E{{P
zC#S8gM}gAApUYgb0o&(zfA+x(P-%nBv6A>WK4Z21>|?D-$FRL1QrL}+hHMLeC|t-D
z^G(le{0S(*0!>_AP>%j&Ne|f1ovREnF7{A&TtA8<*R&f#6U at a$NZUZ&%r%M8p}pf(
z6Z|Ljh!kV)AV`q2`R_Z%p>plfqtjS_3$8ajJY4#J&3)IdT_db4PSuf_frn|-2lMXd
zGwLpFiWc*S-E<!dVq;?~0|tS-0<Sj!LhcOj2!P=Xc=W+>bK`q!>wW&)V1?S)h!J7G
zc!G?Ep9la3posi1)9okkcRi$}q{QPxtiAAOS1=i=Jz7y+jdxOk0*AOO3 at l^hL9VKb
z%J&pyKCH!TZk8FJ^>B~(2;wuQ#m+2v>{wy?D6tRnNfgJAyGw=f54h1e+yia2^>gIt
z(E%^M-qKXn!gTOEbSyyTO)-+^*h%vB?U`qjDY at xjNXG$XXegzf-v$J@!RHR8<o1^7
zMA&}>%ob1I!t61Mz4Tb-dze}36axcd6kb~JKSsKHHwso at q+CFk`BmVn367My)m2sS
zZ#RGsy1R!L`1u{#D%M||D$mfW1|GMUj*brNg%GB;sEDt-d;HJBXLAfVN at HzhGHt9Q
zgApsn+SP2mc4-ba2IZ!ym!wMP+2ir at XPcuC@d-|gQ(br#4><7WTNlt1*RZg#vZlin
z(ptRsE9O{uc>1&mJi{0TUY{))z;V;g4sXvz>VrSQAQDcase^dLUey;bUiguDWZ|XU
zOn1W%7#ka}EtPD#!cQ|j1 at CbjZ<1s16R$or`Ti)8XA7Mu1b6Tm_kvxyT=`*n<Hk;q
zw-CE_J32Z7Suca7fp8+nF9*i{k&;8jeRkGFL`;kw!&=kfIGF_O;e^QM;^Klx^YP<H
zJl4l}FC8uVrL9eVoXf-AUEDK-&-mfLKO;Cd8_#9`{r#l{76<fCfj^5lE-`T*gweM;
zeR#4yI5>!B at Wa1;?ZRVRyo(n0wfyJL6Jh)oH$X9BzkryS*x2MGyWJ76 at q1o$S7|{?
zFOSDXAYx76ga at oK#Iar2^z=W-s_zcs(c!Y0e*(N$t36fK#KZ)k$;Qd^7$?@9<FO8?
z&+j>g%CW)>GTj(5KigU*Kgc8o*p>qi6#35sk6_vqD8PRl2{r|14}qnFHPLT#q at u_(
z3{g7TbAy%G1OWjlW{V)*#rzH)=;E5ae2GPI2#S9juli_~zoh5lBCo2dl5o2PziQzZ
z9`4<Eb at OD%MZ*`qz7+p;6q$VR=a;y9w;JI5&6_t)Son7Obj`s8CuMbYI&W|9>{q$5
z5f6^v6#?n9x>{aWw_m?48KX*~^V_$0*=8h!yp95kq~-zgEo#J*tKZIng-_1Jq<A at y
zhPL)cb8)#nup?!&1Le|cYPi99d3h`}hkT>3ZWEXrBNNlhU35PfHGMC_XEgC(?dCh?
zyVwzj`F`MRa%@Kn%*S6~FTu9xqY<#&wxcMZi?0CdToL|To&Xb}=jW$FUL-C~^6=q9
zjYp4McI+b70AxnR!*4acjo)&20jtVa7AEmFW2{0$d{~_+Impb+94oA!^(ixxcVBMO
zrXA=UaaH2ZEZu17ys6<~7l5x1!fs at +-A=x~zGY$%Htx#8{$ngzn=*Ek-C7;K-e<e8
z-W{Zi25 at v>lK9i|+Wy~20Md9|e{6%cv9SRp^1;Gb*qtj_OabdNKivbw!2JjXCFM(a
z_*17(6Tw#^Y(LsIni!|DvKt~dZ+^`*jJRw at 4E~><n=5wj9{GP=msVC*V-pi>G#tRI
zchtQ<SN at We`b}tP@|M|H;5Fz7K|pBA0Lt+s1>X9~=GtF8I|ah4si&8?wMz3xKpYHp
z8JAQ7KKp_R?LEwd{bLY9-I^nLSsy=soUPp}B`%H?p1=fLKYcoHtt=oQkpD#4<`O at D
z?bWW14wh%9VZGJykO~4CH-HghwHsb&{PAvt_t at Bz`H5D^|2R_oiB;ep7_6|vFHpcL
zwRzyX78@(;m%OJ#CM)y0*d^54+Y75Ue><U74{h*H$i++MV{d>)GFEKS$SC3u$;k(F
z8bYzbjQqq25BQEFl$2O_3Sn(_Zti(R1RYlQ96o$Fwj6v*bPQE*Ft7gB@{$HCrC at P$
zy5pt;U{U|tzrVos#56QcVKv!6xf?|8`&(Evc0wjW4xs`YhOjIV7P`F5bw9<CBQfSr
z>Tk%)Q_9H542zkXeyZBc1jTB2=guAd`r>m(pXM6((E at Yhx_D83_6dBF1D;WVv0~4z
zR<$>5HiL~*VBm2)3md8R59HN<|9?)l7*Mjdn_WH`FVDtie&9bgAdahD_8!J50KdC(
zeWtitlI{N1^95cuW8zRNA_0p)mgK(8l1Sfxv;x9{t&_kd;ZHD~2(UNSQ^;p$XGcy>
zPEc1@&ob<iA2)$OOVN?8xeMtD(8w(r<ohx-^yGhy!=*p~@FpMxLeM;DLpZ2BFeHSX
zh6$dR^)|_K=RF4zvSEXra&!4GmH|Jw20UndHaG>sxmVOwuxmF~sm8~@$u;gN$W}>L
z{rs#D%pNTpg5`snJ7D&}1L12J+1U082?-$-FZl)qc@*2PG61(z&NaIKU&z<18f}d5
zS(t3s0_zXF^fU^r=&;O#2Og}lA2s at l?UCIPlTyV1JG!fCX?amaRh9d3?Gd}V%46`U
zUPnB$JW&34K(Gdo9mMWo>p!Ql_zW20B>{oD-CYpn$J$b;0j&MV0^oN8!3;GA%3N#e
z>JS4M^hRCCLBD+2SYLfQ_MJWy{FI-cfA;U+@)}=8Zs69>o;_pPS|DDHrluxr!iNyP
z=h(4hcFO~9SV4V&l+>ThOOO0UKg297%~w*Ql983wUk?orS4(=M%+`^rRy6g0olWHc
zk6)tUi*11Q_5&k at hre{`5)05qU1 at pwm;L=3glme5$M at uH%eKn14?YpGE2LJyr(+2(
z*avemL{A*U?yx$=2ZZig at v$>{iM at 7z@8Di5q^dl}@DF}gx$S47i61@?q@|^?yA~N_
zgwR%BrLuB)b at d)tAYhQ#NDTC7YmJWN*sofC3+DAiD~xf^%DMnyYY at PMgp~9q_)2Gv
zp(h2S=oVOCctU>m+&O=;ceoEhpj4Ap@(*?88oQ;WoI^2*<DzL;sDi+3SXo)c at 7`@+
z&bOh at O-<#1Pr*KSc=ggRU+|u9r|4)#e)BN`E)&3!ogApZy4*eoPwT&<ksm%_qE382
zJrQ;r0j<Lt<|kb_DZp~Jy)7Fe#J3Q>g94VnOC>o!$CzK5_rp at LRt5D+2=U~H4($Yj
z15w_F4I3?he})NI4OSjg$0%R14cILr at p5b`Mb9W8D2TvH!4AC%4=3BTYZqWWHG<OL
zzs}eUZRp`1OpjcuDN{~P&Lusa>!hH~5iuXyN}$G|J`RAU#c~Ycmj|vlh6_eUM at O5-
z;};OceEf)gSIfW`Bje)S9%O0{^jP)XoNP-0BMK*{C&H5c(9qW{EpnLpzI^!vuG at C8
z_H^%Oc4K2eed(;J{{B<PEF|kL{pF`<+C-KP^t=3LrVcXxtmUz2pLClAz0fPxhXidG
z<^JbxA9D=5USijMQd%)KZi8SAKkqMrJy?TFeU8-tn%H at SixkDi&W`y)*dJ_!MRz;7
z?QX&8;K5k+3hsb`Uq$6)iykjf;{Kr_JiA{83ewu at Ur7P-YwNF!=_V#7mcg@)1bu>q
zh?Gg_A0PMl;=D&|w*SlQ(vlkhN^|5P*u=x{- at S8o++6Kb%r&Cfw{IVpVdn+A*)Mxc
zEG^~vFW3-ENJ@?{7$umjg6jD58i9y~oqZQ!Y<xV at es%G`(sfX_ at pHz&ppFPp0Uy(E
ziI>JxtoP at XktyoE(N0*M%I!l=_+MruN}CPH$d at l)mRI{Wy-U_cIN at y@#c$r+UFAdP
z1ER&n&8 at t=To__~c;HcX`F^_1<vlpDJx3&wZ<|o_GUDxB-rnzZy09Vlm8Yksp&==D
zxWZzzw;xkeQ{&?1mPV~%^CHK%&pWr*W`AX6<(8hkQvp(&f{N-Dwq%1=#^XGIQtoH7
z8V9H at kN`(}_;f!aG}f;5*)P#x$Ny<&=AqaCwXzqx_kZ6?iZHr9*4D1A?DFvNz^ZU;
z0X%#dB<+jZS_y(vP|$;vvZ;xyJdRQ#cf343_p3RsTUdT#($heb0L4OzCodoGgnaxT
z3Azoe0dKg%I#+C$0o>6^h6lj%KpPwWsOV at -W8*YH=7slQuD~j=Ic|AUeG^k>MB6ef
z{)B9TT%O6mPV7?uZ{EhEN2TAtlf!~wfG;Vzy0*5ap|780o(JsXARtF|%&m8&7YHCy
zQZaZ5SRO#PLIULHM`UJZ27LQ#=HtS`!tJo-dplNUAVA&W%d4a8<cAMC(+j`U0C+!s
z$>c+Gr1UO80N0k57NyFr<`N*m{<+zinLm^&dSWGyb^QFv`V7Q`Pij=uPEawZrf?u3
z<`W$kXZn2RhN_3uH&>2<28#c`Rly`0&aBYXq-Xe}J=E~OS^!4I%TAAZ{39ZKoSX=R
zYa${<fQv`H-N)wk(sP-Q-Rmy4r~cI#;RbB9nO^#E=@v3GG`|T9WI1LK0e(6+H+L_o
z)#T~Z=V at u2J9q9xZ-U4>Ha5nB1hJK3%ax?D>x7L>f}^$=mX%OiS0~9)3k2mQ&f)hx
zgxW5Ue5cz%x+)MvNK7H<;v6Gv_%gS!@F(-3 at +d1SQ*!NUZp6Mjbf2szO*`=v5xzPp
zDQPVetmE6aZ%2goF+_DXL|i?{$;k<Nqx9#`2f+SFN<j`|0{UB+z_AGX#i0M6?&OxN
zlTu$aC<RW|KRgTo9P<HU2Ug5hR_+D{;*^n*0dOt&9zyUym{sp}Ik_V%i&IHpFCyFa
z;!eu~>S%ioMAg at K%uj(yVtv8=BzYV at f;~QKYu|H?)xA&O6!|YdJ241vig~FU!op7n
zEB0bZglwD?n(cpuj8Tv4-f!!x`3~%N8L11v9<Q;Mgu_B>v%)x}=$oO;XA*i7P!TF6
z4yq2Q*4xw6t5cM7Kgc8y2xMerre7>UJ~U<P;|+Cqk2Yw6eRR7icoi^(^PW at P8*CRf
z@(zfSm4Vz^n8?K>{jN9!2nyea!0+5~H{fHAUp9 at wbSjQt!YuOc-2)KlwA4bV!iI+*
z0prks=fnv at p!$Q!w;`s9F(Em*!gc>KoNM6jP6E1uo`>hKsHo`JkGO-dF8!b==;-J=
zIy<)%#X24SJ>QF<^ee!UvGdEt)fM}d+uGWWb7?qTQNF0tgz)!qtTS65b5%J8KX+o2
zX%NpIoJx|Mf-xyL at YSn*r<s^)Z+795fCj~ey6(@m#|TJ-ftA$V>}u5+$jQi%UM6vm
zPp}=24ImIo*8X^w`I6%u*w~wki at dy4vRS`#%kCLuz>$t3x98*IGx+)G=D11xj?0$6
zkK;_nqK%bF{M{6AItwP3QV2iT<d&(^XiCtriP{d;06SZ*BL>UB!9jL|1T#)uUDVtC
zfv$yx0$b)D;2!_rAqv(V*p`DseSozOEYA%AQF9IoIsxlOe)8n&U*Wc7_AB!PPpZ+$
z4bWNj+fyq65AgsPW#Zq_`k`yQ^S6gbNA->vDnxh`f_5QjXlX at 5FrJ<!!|pc$0iHf7
zk%f=)NmzsBxHd!wy#6CN2(LbRBvx8LK*IqpySMngNKAZdl7d#{tE29op2A7;DX`sC
zfuC)c9(LzFT?T=b at uZ0Xf>h!=FKRw)lE$H7EA!*g;4u~dc}#d7pd{Wn3Ly-oEQDEU
z6_wYN=afFEmpI^30AB$CT^*zgzf6HP!FzXnd>m&n01%)MIeq5LmyV9(NCe5qs+ at N0
z!X(*oDk?S_HZ<HQ9J^%wR~wTey1Kg0fPBvlxY2!jN6dO7MV|4 at nVTFP#ik>#*35vS
zxR7Hp2CV*-D_8or+vdzC+eiVSB;4+bc-4&mh#Q!i@&YS2ANzhC at FX||-*>RW1LuFV
zrK;Bb&p_lH9E3tcLowfXJNybjPyBPG5(hz?ss?fL;r9>MAxh8VxI4_ at 7g_&JHy at XQ
zAjR~bc$#lXkYlT;s;YJE0-_==F7AY_ld7r%Lqm*>dG$YYJlw;*r at C^rYW-N_6BA`n
z4A{KF5jR8CKG=|rJ?bD$G at m?icUWK90(}z`uA<MMH8Hy!za`M#f<OZz{t5tU0Q4ug
z6`|L0)Bg2w?iZEkTTTHMVG8}f^2Uc+T2KG|c?=PdKuAtb#zd3(OfSwYK!pMec1eSc
zLU#d0HNaDz`S{!BNOE4~5I*Co%~i+EgLsh&UU;l8wY@^!%n^1kz^ZX<#k%$QbEeyY
zP9PI;0^j8)dg(yA(6nE?*cmEl`~L8Q at 3LREgN{r^PoWi#2Z3J>6|z?fH|zq*jnlC_
zbL{QxG<0<nFdBRH0?V3f5STf|$$1yEqGNk&8yc#BD-M_a2BtqYIT>x-w`DV>&vy+>
zPVSeLmEAh_VbC)NFvhc!lb212jEsyhmx9?zMn*;;Dv4*9nfE{}0Hc4mKX+(w5QobU
z(layRzhZ-OdwO~_wY5K*42#5^26VtQmE`-g()>rFw8!!IN{r6l1;?cAKgJ>V|D=w;
zeEqr?p_u7TFod3#(vjg|Y=U)ga9IBLD*~9Vc+wfk_gm-Y7@$T+F<mUOeZ=QZMFcF^
z^BCt~OM)yq84~}%z<BPi985ug_n^7ZVtyw5+1$e|Co;{?&nG!}Fu?PZmX=mzQj%xy
zXIsWgmyTn**?|Kt at PD^BEd}ohcvU>yv;5t$<NrZd9oG+{^ykjq;pl$9@!51_tI01q
z+S!{%K<|i=75I#5q2<W!Ln8I139 at l`sc%Jv$fe7dS!u|mGo$2_aF`elhEsQ3=S40T
zE4;lDI|<2ZpQ&)#IgUB8x3|Z9LO+N%tntT5m$!q?1KPS at KxPH<<O^Wj>}ZHF8x0w+
zX0ieOR$k`1x;h=eA=aB_Jg%dJ=#P`D3aw`Fs#_c at jlm!6Ha#b&YIdM}uF-MhFg-ne
zO=F`1{~D8|m>5nv^x}W{(&PZTi4QwObsT%Mht7 at wYyv_QA{;sxj at tu;g1zdP2m%tv
z^{|43^2t_~S`)rvd1G}3ClcViiji9XvG0+)MjOI#F=ESYAITKown00DwR#3&$7Ey8
zI<`EC$FLgcx0jIpibh-VgG)~bT^@4to(2rUH4s|v--VYh<0vAWxC3v48S~04-5(gw
z;9>f++tr-o;+U}>6MPhB?redv?UE6mlIvwFjhE}T1=rdxUEf$~FDa|878~c%%ePrz
z1C2p!Y-~I>F(K5px`sox!0>J6$98~c9H*v^mJHy)sdX|kGFd{^5Ud<myZR=mH|((~
zlw{w&htDrI at yIJFy~3Fo5O8q%(EcsW<sN_iXNJ}u at bG&{*9z%Jg(7lZ1}E)wg7?Pc
zGbJTudU`sZ0$I-Vs{Z;_T~V=>{{v!xT~XU%T5Iw5Pw_i<T313>M-s8+0^Hys;9X5^
zt<6}mH3#om(s31==Ks#1ZwGQ+Z`wHp!3FP at PEpEkuHn?t(HR0c?ds)qCuOaqdB*pp
zO at ok!%K8s}oy+eqDNMmj)ojouEiHY?e&qrv1mD0w5>-`Itp3Jev at M<igO8S6*WDM>
z7x^}qbipf<{^TxcVVpN|R<+<rt}zy40u(!a`*vGIRFq*m=Z^E_lu}+oU?Dzm+zCh>
zPO7?i at uGyA#8$*UN=b<;hQl<QfBcvm^yD0LJ}7i8Ldf2aEJcdekB^s&>)MkiEIW7Y
zikK*l5J^qt_}2$=R||v|sKdmijftp?UkvPQY<Li1T{VS6<hCc}k>4n7X*n`IJ-t=E
zZZWwv5~i at Qyth|v{CkM&$3MNFBmDf104>yjCkVWt+brAFIP`t7(6Obj(s{$-{rmSA
zY;BgNQ!?x@&&92^)sD9~)pfObgO-Vt^C0lcjv{Lw*xp$Y^$_=IPGD(x&V;#IFv!xb
zE=)9UA5Oy)fzYjjI6p8l5}T2l at k@yAzHq~Ryh{_zZNt7D)Z|QL+S~_~`|xd`0q}vn
zMh at 2AKYHv~c~1|`^XJbyK0THUTdX&`WlcJ#Vk6^1j$Ma1stZ8%!%a`h?1v8Wzc5Si
ziblY)<<-ncfO=Kq)lIv;U#ttXx;4Lk|7d7!{}noNdn?qoJQ2?{Yy at 0W&NaGu<HpYO
z=g-gfZEhT66!WeC<}wR0q!hjvQ_F}JX^!jr#k@}r7<BpEB54{sOn+zKxO=$E+qZ|d
z4)%ya>;U+>(>W~f_z`zk4EQqADbRQ)z_Kvh;0RD0&;`UF69rY?H2<*%*9VG<8ofo<
z*xiDsUcljS)B%>cg2_<b;gF~e*=7X~Yu?buCnwu6Wgfe7tJP-+z}x-x>k2$t0653&
z^-z_U{sqUwu_vgg${>#5*gm24d08y3*t>Tx_IU%B!2wfP<$3NLDbB=tNk+48yK$Eh
z%oC>)Lc}luQ8GL{%wm7(j=Ze(OdC#q2F%jhsz;IW1e4y_SSlHUm+ at JrvR_@Sgm{D9
z+}IVLo=#rvLnqc(Y>#z5+2uodPlqr{fP`shwoAd&F8DIOnm#^h*`uI-fv800<sFZc
z3Ii<JN{sPd!hwrGq^RnhV(!Wc*WW+G#PqN!>L$U-=jz7s%a<?LeEs^Rn$Gb6K{3yS
z{_Wehv at 86dZ5GNB?<dW|AAi96xMO2+E+C%mba!`)HQWyh3c{<YJ8}%EfkRGMy}5Is
zF;*vJ$q!=>j;nK68E5v|X?VuP#ieF5u~jd>egbx$;Pn0bJy7Jlfg97L3!%HiZ>MhP
zhGmA{+KR!!e%Mo1V7_-dQ&|Ow|HjYnbYspGC}BX=hMXSv3%SyN-NWDGc`yWHaVaUp
ziYRSK;Gw{?1_lQH82kis%XRUhD6AR9cDEKz87nKhfhlr48FqR3@?mS at eS9MvFy^>n
zhqE?8M at hKJ*RB6Cw3_a|-j!phsi*g9aBxtkU?y%(iJO}n at ENaE#5w9X1$CiU$6iZM
z4+~*BIuxx(r}FYH;n9M%wKaiYYikQ44(W%W&AgMF+nxf;soLj6bh5Djda{a&?m32C
zXn_Fm3MmWW;o&&x1uxA6Qaq4QaZlmyiWQFg16#tdk(HIzwjv!ZN`Ln30a$u29v)kU
z)w8Uu&q1j}ICJK&e<>`o0LUp+v@($doWBy_UK44Y_?FBN$;K8$OH0e;#V%w06n&}u
zMz at i~35GLgc4M{_9>!$6IhL8S>+KoI*{{qI8qG1c at VKz50BhJ_EpYtldk)nV#Wct9
z<}>qSO*n*sg_SiSdqXonu_;<?XtX{AtAHXSBirm}RctQQFQ_E?fJ(&#7G??Bg32Nm
zF0)MCO_A4cId{b%|Im!@XlPQ}%;mbEtq2!?i<^t<jZRl at vAqB$u6%u;V|@g+t%61x
z`I)1ft#_s|hlYyk6)@V^az|}BwxjZCYNWs?6crUqOG^n;o!R9;@7%%2tOaOj!jxQp
zH-B1NvqL?Zo|Pp60t*j-2&F)Oab`_mNJumgcg$HSO%jXSc!7}=%~esu?_#YzmR$m%
zwT>UhI;8%74XpO7sUZmyu&R2od%s?Nu-f0_6E?q(9zBYu0yc}k_d=Kt3=WRcX$(g>
z&Bo52JuMt7GZ5_~bOYiHC<ziEv`9WI_-Uhbt^+QQlY`+O1 at b8yC#4BiiKTTo`(t3>
zIlwSBnBZjEXU{I-KtozuZ}eGVH?qfD-Yw0Dm)_vn*I=dRSXVA5&LU-IX2wgFytG9%
z4R<MlSw94Ulb)OF_Wbz{0|Nsb7y_(ZM60l1xLePXD`6u(HPyPrZTx2 at GbM)#2?129
zSjvJ=u)1Bhini*X*VW!M&Y+xr|Nfmtsrx=40OB4k&T(H~>Z2Q9n!WI|qvI6NUQp*%
zRbJFM+qb)01*e#Oj}*q4jY>HN9(b;V1K?(7o$~W9+bm4DBAr7gw3%l)&%@&{<R_ZK
zT4ZH4)9VFcfQXn_PnC|E+7p%lX*~w4EM3{?e+Kr)b-!bZB4yIq*;!gn&IRyNFvA|4
zG9W%azI1;4M?{DIAz#ZIBzAwkGGo#Ux$(&>&))o}xLY*V=c?((dJ30z<t9jmXQk$|
z_BXDzKycX$hJ=mh5bJ@$Q{}(W=?VA?*1h at _@DWHd`SIf<u(mkO76`r1Jk>!n(9VQX
z{j|^W`e9kgLOc(wCR+9qrUkx_mYd$6Y)cU~H9Zgf^Y3^2IhBH|;gUX_5GJsY4H&>|
zukB2$l6rt$?)+v{6hqPaVpn=b#`EH0hm?g*Idugy0pLw3t4YOcniFr`()6!d8wQ)D
z7We*2KDCv}*Jfj5tAGGh*_o|h$(~lkz;8ZwihyJbDJ#x-p6jvd^8lF^DWBv|wv>Pa
zSBacfg--Kss at wmJmyXsGH2&2P7Iim5DEqx}cb at d5{%cyng0>u>$%7CLm%Og0$a^Y)
zkSNXoHF9V8*RQ^%e- at V)KYcn6xcaQm5Qx|*cnZBKRM1MfefefOf}0Nkhd_se{YIJ}
z7ZJIm^z>;4u%E!?>Z=>{c-{!oj)~w7$`S{lc=GDEx&QsuxZhtQc3V-;`^Cn^dAjaD
zwvU~i9e)?}J|obeSaZc2?w$Y6`%9bwP(~WDn*fiYsNWTg*H$Apw?>Pm%_%OnzG^r)
zG_;#A(BFTG)3$s4iWJ9R=|8WFi;H(UDQ4^4PZ?4yv`itKT3MW0EgdqfsjaKKdHeQj
zEL5p<qjO++dEf*?XO1EJCI@(8<%jFe6(H>^t@`Yl8zQc9*4EbgkriIoWt&ko1WkLE
z;2tgJqo-P8HKT^0qWSP4LrE%F>~UT_KRk050m2sMU}a%{rM7S2slT6}``FkMiX%r(
z6Wouo%RlLv28Y|}6v%I(_j!rcg&bVq=sBe<a-c5u3~6E_O92rPoK>$+Fun$Q0&L=c
zbwFd!K`;^#aK?AaBpvhJmGo}8n>SsLpMO-d%j|;0{D_ at mYU1e3R9u9^p=eRBL&U_y
ze;jV#xl>u;akvupcV at NFE>s2JFNKKU8x-_l(t_vwd7 at bzI{)fy>%ZDNt{tGgqByxy
zFr2vYuhDUzq@<*8SXhdMVB?voXB at 5Su&tmjd}in8k5pDxD&;2B&HQrs^`w5`pv}75
z)Z}&UC${TiU(4IGMX!a71||ixEQLPasvDv3UH<#>Pi~*X;hB=n;@PF80At$Zl$6RE
zU&hL-oB!@o96H2cYG&q7w&3x`dnU6{=rBaQsKi8x?n0}(zq at kp9yoDcq*tno^mSIU
zabGc3q(#NVh!idL+U|4$5%j&F2&9IRI_7`}0S}^zic~`REhNl;cSsY+_fwwDGd=);
zC7W;xI1x^)xv8cW4%k5s&Vb1RFp)fy0s9E`04?<#gCN2wz%N#i?l_;~rlw|ON3ne&
z2!=yW+HS16bW&1McrY53oP7Jyqet!mYBskB<Qp3s3u~Sft$k59Juz81?=mtx><&of
zv3oxy2^}3>zHi0XueVT$W8v>$tv~yAdRk;38w(4$y4{?siMjcam6er2;N6t)V6qoJ
z?y<1s0#3;+75XNZM#wG?ODSsV>JGE{OW-%B6|YD%9F&-NXYAL)Hxq{~l7v&YZ{MEy
z98vxCv5-Xg9Kf+ at TpV_%@q!796k1LZ5$3v#O9W at 1QAZ~ypQWIpV#9+aOi|*&pQeGq
zSptc at z5T%SGzXJZC<~UkWjJm+_+o`h&8sa3=<ey+?c}y$zkmP!N(fCXK!Dli=jVs>
z&YnGsQ!7No#ogPUH&Mw{rv<N0bYu+e^5m11ks%@gN-b6Q>R9k-e<O#0+BRk)A#ztu
zMJ05sGdt-?U-4)6SFdFL%kD(ENk~ai5-R at v{gs{S=yyd|)l3%b_N|!!Ze&eE1Lerb
z2m>3NMA2;7{u?BZS4 at S@D#Xep;FkxUfBcGzk1t>;AuWv)pUz=zPE1-+ at mXJc+TB!%
ziGZkcxw6vI^aK)N;c_?H=!<i6b7jCo$HvCo)6=<9x?0XC+?SI(4dD-Sj9IC_^5?H!
z2?$r|l_jA^#-^#S&yJ)yuczF!sCk8qhTpx<aZ?q at d;7nCoozkFt6~o}Sge-|M%S}-
zrSexM!YeM<ZRg<_&LN0HB&-PL)vrJhKS at Kgd-m6uerjqe#>+tvV26G+MjZ3^_m^;6
zt%-S51u_|*qk^x`j?oBETfcBm&snuRlj~~l{rwk8M at C0SmDGm-9;xbLz1 at wBjF!A2
z9XB_2I)NfLPkHvExf#|zRx)5!sDz_>dVHLf;2z<ylF%juBoJ^pT+p_yQ|#HZytZes
zgL<FInlcw5QavptFhtn^i4DedOy%nnJR(ceJ+APW-<z5!KnWZ+E?Lh($|!#4&Q>8Q
zgeWKAKb7#YB8}mK0M>wbmQ$TLDI(nZEU&<&vGH*a_`k0(3;=o1GoS_o6!oGygU~y_
zu)rIx0~Tnk5MdV`gOL8p({uNS2Qlk;tJa|^qtT-$J{J`M{(+dX4!Yo(6A}_~FRAt5
zmEMn0pFYV{R9Aa~AbW6Dtw<3orcAwQozQEqU%x&WX4Olv37Cc}e_>x&NFpfsR?!Kk
z<@6J|Nrh&=yfHcyIgU$5{8XKYDyAvX+o16R at ckCVGb3PDbfKf--4mTl4r_Vv?)fvX
z6-DMB;l%qgpgllbUcG*O;C~hOiSIxZ?6?M+=GNqj*w=S$ynL<EVh49zGw3N;D1OS?
zv_&u=7V4jr-P~B;isj%8v at 4wp3t9X0In|5Lg5AMOKARg{HBx9-X?5KAOQ-qh(dk2n
z4(X)~{b!1s6aMT9`@mD5{_e<dGg?hQnU=ND|Hav7ZEep&VcO*66XpNM0&Tv&<JYm+
z;AGi)L>u>S-)>{k3r>oJ1u<Ft{QzsQDk_NVSLUz7Q;mqZk<s{(Ej<Sfg^8h<xH$c@
zbQcYdV=^(zsE3LGf^-)l-l6N%IoYjB;H1B%hG{rs>+s>j*4DRf?fv%cn}&hG>*i*e
zltw-t9^qTJJc<tL<}aI(vk{ztmVz5(p*<QseE0(B1txRhsk90KtdQEnvz$3|hH4e`
zx(fvHs>M$42=Tqhi|y>Lz*FI|e!MgbCucPd?fWx)3)4pY{8SK1U44BiR_4cd0qBBE
z$H_e at O4%}7CBF=MA~%GE=Kx^`VXaI|P0PCTx4<v!Nz-7^?~>GiiC{q|K=;?~%C+?i
z?J8eX?JJ5vfWUM;@GU1$^)^$#o|*>v1nYEX>Kw$e*&Q7n{jj50i3TbhPZY6 at 7Q*|4
zy-)UKK<sjyGY{NWGJs<MXgi+#NJ~?gTUcP#E3KdnszzE`T8OMJnVFeuR=U4~Yi$*%
z1qB7;TlGLZUsq6AdS1=^wYIhlR9*%S@`muH)>~kq4=)fWl{R-NT&|%cKAr>uM>&Kb
zEXTvSv6Jm-Y1v^)DKEprY2oXn at 84hOSY<zV&K30O%F>LFVOLHovpGY7&!0c0SahLS
z;vm>-zod#4J$S{i+s!QoEyuif at 7}TQe4d7I!Tp?^X-bom!OZr4-F^uPtP+xvfUP8E
zW at af}Y}?j-(XjIrJG<mYNp3D5)16?Ky@#1<{$<5gwzX0I{P`2RpF=JhUTAx)tzFj8
zFzx$StEs68=Xm<Qd*_U^(TA$Mmg*DQcoEQtRi?aY1TM7AsjSe2OD;6p?kpWBcf(=Y
zcJMa>1+1opa~(VS?kcIPs2q@${UK(v;k@##y?aCY{Z$Ft;#6x#!(ipqja%WFXPcR}
ztpB!NHkCrE$`@eLBh at 2y0k9~$1q1}<C)*F=P)T^^qN1X$($E?z(3>~`j7O)TAE(IL
zEpz?(`xh&+e)oQ+E}e6lwfJ8xz>mTQ$#<(oS%dy?H7|B9Zl-HFc0anUYMkfc$7L}g
z2TbPR>e7q?rY?wCd3X-vy$k;~e-{4Di=Vh?(0^>TINqJ*#ls4r@=`*o;A78+KEeJ<
zALT!4eh at mR{eZ(z{$ozy<5#hP at Ab8F0wojDsA|t25MVGOF=Bb~`x{rul8;&gwa31F
zC*M!5{`;mg|1Icpd+CmWMXQL_S<U~hEVzIFzRA<4=-*pY0A~8=U?nIiDNU>K{3gm0
z6Y`kR!rEN$O3VX*9|7gNn at O7+xxnV==!iqz9XD6$KpPQTSy?G6Dcy+s at uj<at)M-x
z$3`pVWBlrR*B&}Mr^{xe{lKlO!7XOSE}d(NIp^Xe5sPE at DbFerV7W1$Vq#?_CM88i
z{dq!@>$u583+riSW=Ya+5MOv6bAWgkaPioeQ=OxRt~YjwiHYS$^C{W>>rC+p3!_1^
zla}5gDOpE7t)QgzYN{)DsBosZ9B6K2`{WM7e{<ikn#YaSCceS-(PmIdq|y8A1pN(c
zH}jzt{GK4o47jJZ^z7!%-8kP)N5>5)c+cWfJdws*aDavub)1yu!O^am8o<%f05V!t
zuYCm$ZP%bh`}z5mf;<L86nXY+*}{h}OR6)lQ_k#STklt;2Ibboc^kpS=^oFS;#D%d
zT1i)zm4M0RgF?%v at ad$ZMSEridv2xZ^_M<ho=huA*Kg+l!W+}zfQK(Y;kZHYgPcvj
zp0-}cS<>&p9DdE(`tnDquvfqUCqDk8l1=I?wilqa{muO(TZzSALRX+McGKrqb6C5e
ztgP(QM!}{2jB46)pv~0tp)W7$@-067tj4*1RZ2Fk(Aa>cnBHP@#XLsI{#``o!omJ6
zFNx}sR#IYHoay7l8-VA=qJ6M6X2-R_z(7o|VM#Tf_4&Si%P?>Ax%=tlCF?&igj2AJ
zv4(_-5=X;$V`Z(}cB8IreTA?oSezpSm$bAQ(g-@>`ULlumKNgAY1AVtagHTcu`<9&
z>O=U{b7w&85D_9}5~$2yzdRWce+IA48H8tlCqPUgo%Er7(p5eQ`)6VUxinv(7 at 6l$
z*3kF>#PJY_ at 59KH1@Hiy!h)cXrgqem^4y{6>1l{;B3D+%qVevFdds4hZAlQFh_E_R
zT4xpHfN|0&lMA`1sOV0o_=JRfqs@=0tGd}rn#RVQ1osr>+}^pTMse?dcJN1&- at yA?
z2;^b>7KeHYt&R~YenmL;en_6*M3L9gp@=gSFreU~lYq&No3#M1=6+Z~GB+Ax{OV4~
zMGviN?^av29Hyxe{am3f^-)t_pRzPZBVg&y9APJ;sH`kGgTe3sosfz_NBVUfAUD6D
zAMxY6vaniG0z__60-l5L*<U$zsz1jkL%W^|34+7wRBo38DMLZV!esk?TU%SyRRCYC
zR{wG8uF=6khWF=`<!7`=Q{G+tcoFaw1VN8gUk}&2llqs9?<cLqK`_qHEGOo+TQ(X0
z+}_rP_ljh|GgFV|g(<bdmlF}V9oCj7)vVT*=Rm~5-kvktKW9~6)6ybGAcr+A)8Oj<
zbJ;85{)q#`u$L8q+*%U`zw+g-hD&<EUwVq!il-1DJ%JYzU!BojFrqhF|CgPeeUu=g
zrluA)wTJ9v{8B!j&F3nEk{|qb_Gq4dO7>=<nZJ+xmkQBo3_q%zYjhMmAaS|Taf8vo
zz<`JV9?U#lI3t7?P|XebTz$2+wnkZJ4twElG1=w;lye^#KBzjpx{8fJ!p_dFTQa)x
zIKwg|E-vnp&3t@(YAU6e_eo!>7&Y6eY-%CLg^AXkP5 at NL02z+v=jQ|7d4N^`+Qm&l
zLvs#-sAKsrzTwZDDtY$^<f;Yc6d`=Zl(Dg~QCV4X2Ef+MCt5%sf+ZG=*p{?viJa42
z{{NV|4tT8F at BNe_2^mp at P%2wSLX at 41jF4=i%(9bEW{Oft*%{fZjF6C!GQuNeM97Sg
zRl@%q^#1;Re17A7#q+(tXI$60&$-UjxBm3hxcgw!;k+RsA=u?xgXIQ4YUx93;18=9
zZ>p<nB!9l*od5BKGsmcm%UuVbe5}$%BKGsEt1m`+D=G-MY^w%%byr%z0m_z-o at XJd
z!Rzr^@j``KO=DwzWU-=DM~l$Fo0^(7`=UCwysN8g>_E?@p4|DD0Q6|_5c3KO=m~ED
z6^--eyzCk1R_A+>@2be|4-P>C0Ib#4b)plmCwgaoDNs5Cyg*T++Y7xuCU;mQbr=8q
zQK7g3-mlR6`g#p9UArb^7tdRuY6nzQR0hL%FA12G%{>91CG1=I`>Q$nwL2##C&snu
zc0H3 at A0h>MXDnLwIusAdSiakte|^afRp%j&D`;wle=fgP3^0GQ?`{c~i{;Hr at O!rs
zrW=HtyLFATb$PIm09NTR{#)L~0x%<%KHt21r;Jy)9oK#KuXn-PA`j%!h%@|W`g4$-
zPTL;U%{AuBF)ZN#Q<S%}%fbTAPT9D^nBt7HJ?{V<)aaR9KR`;!v|sdw*j#4|L>NRm
zn at -@Wro_CwGmt+*kl_=63cNZ=Le9Wvu>bo~@9IiN- at wr#-9mtBK&lN>^B)ekW=|WI
z76Jq$*U->tDRwl`)X^cP4S&u0%J()~rh<fw3SPkHe2ba at 3P$-07g9c#x#x~Rka>|^
z=BUc@`YC#WKX^gxe_miF5vvXnKi|A&JIZQ*8y-G*Cp2^`;oHy<m*QPkzDK<0I98fh
zf6ElVN+eEXEPqdYeO_Mvt?N?zLN{}{m0VGW12cps*SeoyUZlQs`u<)mUIubo^y?s>
zNuQ*O-XG<9y1F6PCQF6`mmo-7cV(y(QxFPFE1_f-Q^iuo<ZgL+K5Sp*;NnsQoQSDm
z)S6E~KmgxQFi?~bi1fNL2STxYC6SY)bT{P6KrOkju<$nsMc6EUWwbSg>*@r=<e}zx
z6G!_W^#|i$Wk*Htl$Bcg@#1Lq_a610pH{_FQ<txRD~^Bdn%Fz+X7eO8TlkYfu|sS@
z^UbXk4!=thC6Uqh4-6={xD-5=zFzurIPO~V;UBfNwQoLrIE^-n!VgH=I at Ibe9$pK+
z=+cC at _Ya&N!$^7poBMIiL9y3r<J0_~*{>&#Uh|IRH^SxE<#oo-EpHL_y at vO$I4pf_
zJ`3kyJw~1e`5Wgt&i=YJQfqEuQDfxZb08)rW-Qin9XJyi_<)VREGz3+(w7 at g4oz1(
ztEYFHjMby>nJDA2PflMmblZ1YH^tDf0kdLJBYz#PA^m$WknJx1SLrhI`OJ*+UG(&Z
z3kIRtYvELfyM|6f>Pk=kResSO%_-x%cJ0C$i|ra3&0T*gUYu_)bDaC#^XjT|d9%--
z(NxDjLVt>D9UL#RtXSP$eKBbgA7-3c at xb{}?B=!4mwf^q?1lAR{XEkfyM at gCT_X4a
zG9Yp1;N-*#DJshd&wY2RsjUqGL`XqRO>Jgl!wumUOD&5(UNn#K5-;VLFxVFFVr0Ax
zr>>3>4=7Qv66;m at 7^$nzPv(|kq11Es;<vFeZb-fUIE1~hzzDelD``i)yybeYfyL{;
zy!`YGcLWZR>YqDD4!8ioN76Wh`i9jpi6{fjd(Yw*oL0ZQG at J%uaXSI8`F89UJkbzy
zOxv!jNX_t-hvwv3U0FWV4VW$Ff|e3zYcl{|&s3jqXlZFpj)*cF<hG8_p$rtm at oV$+
zR#sM66*ph``#UisW30&5K|w)*<H!*$oPT=!<VkyUO1^7FXLNpkK9=dYBi8CpxDVmz
zb)V4VO%tDvFl$?9xemUg*>vLbmoM#Img<eorh>sSF?*-0sbzwJrJ|lkh;g~+sd$Tn
z2M<nHl28Xf5w;G&!~~?$Dn6mp__f=(wxJ;}hP0_~)y%&&FYQswpBjh%-T%cpepk8Y
z+<oa!+XH0r%C8>?$x*3!_;=Wwk(64>xN8 at AfGj)xc7>#a^*dWaM_nYaVF=$AtM{TA
z&X<>Pu8%2;KDl#e$EFh(MWV)8Z7;?a2o)Y?UvbDWdgYO?BU6~4PqWbDZW2pjUN~#e
z^vO7rw*M!2JH{sf9dBxD6?g0vKLbad{q=hE^HbAqHfJ#tgQtPXBsGC$7E;-d8q&%J
zXJl}%<#~Wuw+HI(9vv$wbU6Fd;oY!1xH7iTz{!#yJ$e)q*3-7pDEpr4*@47w%mxd&
z8n!al-|bpow;!{eT{VtbD}Nh20sK9HJEc5l5B2x=C$wC|8C^0mGXH>pr;fd^TeHEV
zo$BoAA-NDvIIl`*>FSc6KYt!*N+SF at cUOVfIXBV82v5g`u(l*=Cryu7x$)hC0+CVc
z<c(H*!QDHMm{fQ#W7jd#!VQ|Fw1$jrXup5|jx7jS8^wkyYN;s#?#bN2EQX at kPIa~&
z1{sfjYoHv%(Oxe<c**AV_Yd&7LG({d<bZ$;U%+%Y*B2_F_<u>svCW$|CupT7=y22Q
z-n}hCWh>Ghi(<SN*DrJ9<2NeZ!pzKEGElJn!i5VXorOGjuYxmCl-|atPLZ>Ej<637
z52Ks_n6q8&LwOb at H#adZ^%xxGL8$=jL2o8szZ#xUfQQ>jOWT at eAb;(xLHnFTvF-)I
z-6&!Sp^3nwn{~0)KbsE1OV4D9j<z<c2b&Hz#o|IeFfZX}KVEt?8RxPO^G2_CDs^s)
zRVRa7EV{p at riLA8B;jMKM!bYt*U6J7vr7-BA7BvEJ$v>WPM`pT*#Sf{IVDB3D?hk%
zvy6<4)bGIuk7aybcWVX*Gw})tPy;!Oh<Yx{MEU;xdt6s?@7~Vz!)5S-fxd=I@~`o(
z7j&|w9XUpevuzr<AGG`WLeE;4D&(EZPvwO#jdHDtF!-#li2JNawYIfEcI at D&OX*5&
z8$}`FhY#uD9mr+2qu6UeKmdUNk?j!u_B0)C_`;d!>T18x>#O&pqbVSt;j#}%{tqS9
zGqF1r>x)PVQO;^M2HvXF->GUOtvDyf#ujg-eu}sP at fuh4qVNdd&^+&DPmgQYKCs+g
zpQb#wHUlVRL8=}r5#Z2MTF;^+mUY(XC%99gjZZG57`vddv;+7Ono5#8#_^)*$LD;K
zioY`yq|~h44TmN9<6d;wzB~4TYpt}LD3+C%m#5w~-;$)XQIXlQb?Yg*;_5eVE`!N`
zV9{%(rC?qyskE=Ux%IKIwfYN|+i84|#}1;P at vGU^(@sk492t7&&h4S4rA>yn{8Ai@
z3e{bKrnLJ1fJF1Aql1x)i)+Id?w?guB*igbARN`*-PpACSj?WOyQk-S*r%KV%Lnr^
zwo%kFe+9jl=Q^xpxJ2V$y^>15bRJF}jQ`lNW8iCf#l?|a_B;_Pc<fuYR6_8d{QNvh
z*qREEq6RkevZS0H>Bwbc^_n^fIXvOylF4hYJ#s-*H7TbfQGrG*oL2M(E^(O|tj7)y
zY!?Q{ql2LCRJYzCX(QI&-i~XvAbL#y9*+OOqNJO3w!QtcLAy-EqepKV8!4TK8mhoR
zJ9ms49F05eDmrh7t7~6g89+Y%7eK4sM~6*P*5fw+J8(BHNy+mzpQRxj6&UcjNU&hH
zOkSQK_T2B^kGp6tySY6&U~!t)PDD%$=ajHr7tZ4z1y@?N^X1Ex*v$)B^#e;l at HSGv
zO?&q2!3J5T=WVU6XTa}Rk9#UJMvcP56KzC+84QN0uAvGWBRGLf-Rw7vCSEtNsMw;!
zwkF20vb^+xCEMuYD3?8?@owkl<5<!2oE>TTQJ4Jatbswu`noaY#x5MB5)cSVPe1JC
z<&~U{u6ib22d`qf^V2DD;SG+|0 at e8oNO@{%>YgO6X0(;yZfv>6R?x3szhdXzW)hO`
zVH;qF<5P~&?Zev7_DM^#LW-OeC#vJ_OKfJoMR(8?5|R5}8&2H2h!wQn3N~z0LU8TA
zKhZOPy(1_sHZv>BUoq*Vq2XR!R?WtC7jh`!M6Pi;cJhDxcm_{bT>P=x8TpBa;o&qW
zyCh^$`wos!BH`C?GpDq)anyYl_<Q&7(*l>4%hu)O<yDQkKLM!N-EiGgPNlY)eR#x&
z(E}Gpz&k_pN8J?xQ)OaEJCTTU-?)uKMddzI at vmRMHWB#w`Qd<EnTn4eKaLk=&Q3Ob
zI5Gt8mgN5PE8hF|0ltA}5#tjUro*9ez at tL$lZFj($D?IVuh)x$IQ%Qi7rx>K?!R*#
zCU{RJNn1z)%o0~{1X42N<V4B98D<<pP-44I50C?=lmygOvza!+twaJ~p0{;%r`5Wq
zr>Ak&N>x?0Z(x8^)FCJ^kV8=L9tCx9=ga<nb6m~?fg5|4C8=l%o%M2!QCkAtdkI`|
z_ntkcWUPtm^}2`XUrDS7R$;7iuE|?04&24|N0i8ems(k0e`@lgJn_WVty at 3Z6yp9H
zc%fkT&!0a}=omM%wWUbWA}=)H6TM8fWy|F%JF)_sc5YnjbysR;ZQkF)f)fuXGcj>9
z1p{v-;O>Mohj3)^P=rc}#X2&n?_ZoA0JaBDiQh07CO3h)@|S at Qu7!dCgAzD7)Z`#1
zC&y7?lzt2kb_h}H{3llf*qm6v!pIo(_N_e1l|$;rJ}yA3#8(p&6O;fBN;v5>Ng$0^
zf#2h(edP@>VmbK<Wk{tWOq94z<niNRfB!82%m at Tr49mn60$FIZUx12=>d3*h;5Vj^
zgGCoUJo+}CTcLR2!T~J6sm4m|!iy&TUQQc7ZXO=&=n7RN3034&>){7?d-7xt{q`DH
z?P7a-`|n|DjVE$DEFfMjLVQ6>LH(1zuhm`1+$K74{2RN=ewHt=V!f4)j_$uxvYF^C
z^qBu*jbi+Fb_>aZt5Kmt`H at j8f@%Jl8$SrHmV)Dy=5M#bQ<yf#9fwo6FG<_Nx8Z5Q
zW03Q4cN{!t`c;U at iKV4X@OW_gC{;ytX at 6m@0VmZ$iSv8Cy=;dMA3k%4-l>i~Q7q&6
zb6SJ!q9*?zSy{ZhcaXY_f67*ub5mB{784u$eLYIauWCRh5wq{~2OfwUsHLT)@wEy!
zOZnjh5zF1+N!&mN_H*yy!!6mm6xq7i<BeBApFi)8i;Dwy+eSsDc=@s*s!X^|aJ$=;
zl3?i`1qT+V%y2rzqAn}~Ba<t at Tl8V<sYQ{SX6dCJ5cuv%GOYVu<}@6!1$<20N0XYG
z+I=OB1^Y^Y*M_P^i`9c=J%vDnAPxdCu5pfxj0DSK@>l!UMFc5CSzY~6RMEh|K*~@S
zpxV<9ifkQYC0y at I9--NUs1y!92w+ELPmj^0xIB?`WX=<i&MC?-;HNl!q at L=Rlw=WW
z^#kJA%aW3MmM3d(f=M~E9Xs9erd89(C=^19#TYRimH4|`g44+1S7fNS73pRwcza6|
zaD!!4RaI+&J>Wz at El=pQ<?!X>Zs=RL$nmy&3mMr5mRo6RM|SU61j>s%z-6>`ZA&zS
z7%q3-^@5;oD)=JW%*m6RsP>9inn}CV;oK)SM_M<>1>C-U8_sFhF-WS4VSCBpcVQyj
zyHnPF`$i}l2}2jVb?X)!tI~SN#_L1~ELCl7FVqLz&Yanqm6ZjFi-_xT$#6K@%#2+i
z?u2}Y(C!`Y-oMWfW|us6Y!49Gyuw1<@g^%Pn;dATp`igkq at HEnR42~>dqXX+k&%ly
zm)P7)t(~fleJ$r?_SvN3${H{s^s=u__4PNZOiw`68yB(9T$7=w0;`(*yd?pQ_UEf#
zR15;fcQlg}(QN~hl9IXsh9<*zAfv;@jSWwP at w`@653E^EiyBS_E_}Tvuu;f|r2LNM
zR_B^hT4|~Ir>t|2k6$Z-bBaKuj`x8(_8i&L>$7?sNb+EqJn at j-`pr`!mpg>9OPy|T
zP7r}nP3dt|l<d<p`$R-^Y`)GfEDUo9Bw7{OcZo@@EDn4epZn{{Io4-mV}o9roV<TO
z7ncgfi_<D9re<b0Dl01`=$>Eq@>;y^x0X0ba0if!iwGW5R1_2L5QC48Q-5efPa+;Z
zRHiGoO?9xgv^<g8R at K?bv^`)mWc}8`3143VaHRJvWpcmaPy{c$r$_BAAT987*YduO
zme$RyzlOfyB#rMTlBsq*ld9fclS%z`LSg>~FF09Q!vG1R7#Ls!C#6I&J8(qQd;<>`
zKa}X#Ha>@w5QrC1z7jyFpqSVrNe0cNe-X(5SBpaQO^F5oaP{gjIKZ9s^!r8agMi=x
zKX<A-V55g1141JzDniaTxTl^f=jkaS!W8OqT3uZc{#HX{<I^*Ta1N at k&(uFKP&QrX
zO+$m~<ma`|x<3g>;<u%r6?N|;c%HV~Jk`MqgB!RYrK;-Wq=!6F77z-=Z7xxJ+d=~X
zO9=%9^v|9>oBH)DQ`l8fTzvQZ{Jc}0gF9+Yz at eb34WiMIT|9l0ll<<yiDGr8f+%a~
zJlqtZo{IOeQ$JobJJE=*hayg}b+f^D;kX01Hk28+IhAl~GDpP3?1)eaSYBEf_<HSU
zgd|mcazX-Opf*$vB06?`BTsTt;wZJ>uqTdl7+z9SPq$%2!c0?cj3PPk$STDEX;2J<
z<w;tjL6O}aoPddu at iCq}Bu`Yuyc88>kjgFzC>)|%UQtn?R{BGx=YBVDLQ4L8Z*AC8
z{^%}9q7b{y+n%Wrd=JKXYzCiG(AR(5P#>&J1yQo at E?bad(hUepT3WRC at 87@td*%W-
zExswOfg*!!MeycQ*KscQA7ZC+JMiWVPb4Mf0D9o`Kpk+QG_}qO9~o?_LFEGuj?L(x
zF)=ZN|1hQ1Sv at _}C&ST9+uZU%_peuj#x+KY;31KV^m;F4cWWw>;wm!83Z at PYVK_qo
zW{oGs`Oouolh&6Xw-#W}_F7iQj?{H^CY)#lxE>Z32ASQ48IKBeDFNH#X>CzCeHusV
zAwpafVcLJ-0B%$DEJuZyZ98^6WRN2xBSTFMun?!Z_1`9jyVJ%ya+SavwY0Q=*W(p1
z-etwaoR)vr*chOj311Oo^Z@$^1U)^yw04`(H&-C2Uv_nkICf<mGV>H<rf+~0aZU78
z|7`)RO+kc=kYv(~cJ6;mj>uYOWswtj>NFtq>gHyRf9^P;2)`K56!@k^PpRwQ*xO7K
z8}biW1`-z+f5#GJ5L{(iiFdUq1j(s*r*f=4vxFh0bF0|e>@lQFx4E&ims(LR48qmD
z_;_l7PTj5 at Mu36+{QU5;#oaPd*LwcP7nCA{?->k}l at HZ1G`s^|1HtLVix;@d1qg2Y
zldx>vOk8S;D#jM;KVNbeGxKe<uU|urxYi2Z-%jvEcqt`GG at 3k45Fg>hM>jh&g_0gr
zk0B6VzI>@Xcc-)Tby-<i!>#XdNO>1NFkl}o&NJY~WQg0dqisrnQ|;9uW1y0mhV<_(
zI8z2_mWGv;|6l5JI}89H7(K4i!*AOoY*qR0o${nUQ4y!fmF}`)13U*OCjdjfeF6g1
z54hCcRQZwRTh`O6#!DMbD$A<{{(}ZXF*4kz)R+JD0{9r)e9kF1Ds>^Rt*xD!o(|`-
z5Aydvk)j1b*I at FUJl_x;wV<eIwsX}@h-8*lR#-_q!o`_*zP`CRl1t-Kp#d1$x54 at c
zJ7p`sd?{%nrb8(6 at bGxI0T?9GAkum*O%$WN3q(Jlhj<^1%NhH5Xx8?9H7Sf>PyK at j
zBz{%CIYwol(yykqk5OqZ?36X_Dzf*0zl;s{sd1j$;r#s&?X(1U?uBoD{CGDKJO~UP
z3q9`erf`%EUdh^@LR+wX3P%37Wj|k26dHu$ROa%Zxd)OAg*3Pyu(hpid`EnJQ&TMt
zlY9$en_2kdW?3CbiG=}FL!BG%07{1F<kY{g-x<)|ZJ2H*DI`P#I7#b8MtpocKzLeO
zlofj%9gR`!9JbIu7Bt&@=gu7nvtfX~2Hpfv;M9kX4m~Q7jE!w)WMrKE@#O(U>Mcv4
zB;gSeTLDrMi8^qYCjs9=I3 at Ib`?d*SQ;ODMK2<k#HqrH<yiCO+;ScD-?#uJ7R#sN1
zH4A2f*~eXk!{$32AZfPldF!!xR0jNK)I}_N_wLZpXm%3{O$Ts+IUku5#AIBniPD9A
z<KsN;B8g&`R#%oizDMZYi;4;f4&L_aSCiyrC#U=W&}mR&yUjEJwE<uv9y9d^(aM0d
z%Yiw*XOZ&QNi8+yTi|W;d*(aV;Cw3t{*3hX?Q at nb)eZ;<z>`IoeW~r4Sxcfq-t*`1
zb2T4WtdHl>6rKm?NfWj|pVZRVS!j!UFSOF-FJ4R&ah2S+&sVP}$&LN!QO0u5xr`T$
zpFitMRLprE-$6=3=mRWJ3pRLOic?cbYpr(hP@*j#YZK?r;T_K6 at 82g=wAxFKNl3ik
z0BUJ1+bJlfCyIL!&H}oV)z{w*=P2mVEspC60P}YSKYxA%%b!9*nl{}{4GsRrxk2US
z*JE#WLHwJVpAQ7a+nvw6Uq*)QL!<!4P!$yw`~j%(hXh24)8+w~8S*7;()jl68#*`M
ze*JT9s);%_pSM5{kT+r<ltB3`?HU?W$kuUl6T_ZYY|%Qz&3!XJUkDHu;cq!rYh$C@
zkiScr|4uBLST)6vJ3Big$Dlqwdk;`z6y*l!O{*3J=-Sluh|B)q{{1Jwva+&vJbLs9
z%L7~vON)z at Yk*Te^iz@&HDukDXG at qC;k{^ozZG at H+xs=~PIkKuW)+|uK%ao7tEhdx
zapT4r?uc#Mw)J)FTYqz>aTz)s0Jh!fpF8($G&R1KLf}>9;e2_Q^W5Cr_wL{40F)xa
z1SI1mV7gDA7`P);Y&vx_+ac^+I?H(BI6!v<T$mKq)zxv{jhL?e?Z*#Zw8~KkmTLl=
z-pihc=(nS!5S-?}qFLf9(t~)f8FCyFv9-0u7l6iQhul>$`^4F^yNVop`PCZ5L`4DU
zlsQxY8^jvty?c{*%n&`Xc^GB7mKS=5bzE!~KCcTo$g>y#^TS;*_wMZzbp+6ol##K$
zsHg}zYE)6XjVShDSz7LQw at ZwU62HPB=Fq*Z+}Pyj02db*&a4?<pKE7OS0qXU1llX(
z{i?~u4qW}_&CLYgva%)Dw*2E#QkLrLzl%5n{2<S&kqNunQq^PMTwmxxT~e$ftknoP
z6UDNC`vSk!YI8F;=g`vD#-=cM;zJQC>6Z}lt*or@{kc92ar^W03ly(c{@CJXPhQ>~
z0<RZ2a82y(d77SQ5EuL4NzrDzdwQk;#+m*2oWF*0nbyN+dlCd8)LO-{VK6VGtc$&?
zE4b{fBq>}~Jw_Zgo(Vp=W?8XOMr@&qehCQ)k3tmH65xPveH6Y66B84Ze*oG5DWC7=
zMjDb|O>7&z_vlgJ;-W|Tl`=Q(AU{76$PDl$D<?-oOZ%o)(#BOc+aTcGyLZeCLiYCd
zS0=lUeg5*L at bZzwqEz@~a0IRnxB)LK%YR)dV@$+?jFdDHklzNn;jlRi!NKk4*U;}D
zA_t0lz5TGRsS$Sv!rB_81s5leRS%k-bxFSjF=roOOjJS4%k!0edM~O-4nE4xogb4!
zyF^7raoXA3+&s^sjtcjXLCAY7;mQpxy2G-Lee7aLNJ!qr77nbUQ&CYpKI+JdiUR*H
z8)=fzGccHswx;0X*gK)2*m@(cpz!qC!X$)=J+J0IoyQ#JSkeorTj=7)1O`ak-|ot+
zoPeZ(^0e-~*BOPa8cZhwCFnPB4~^)F3l|OmZrjPmCZN{S^zI#2DN0>NIn+9H?7Jtw
zyh!a1Pbc}%)D&1;>|TBs2P1(5dO$omFTP82{Mq^i!@8K4Y-T%$sOYZY=6EuK%S7j{
zbsxvCL-gqBgPiNGrYPY&v;)PbJ?Cub>FG at ur@o=D%26MGd+M<~aS*sCu&Cr`_MwU-
zVCZ;5s;|FKQ1DE~1aMpk#G;<lSYU+@;up$%w7<XR_RH0kB_XpnTaZfvaI}Bsu1H*o
zh>WyA5$TbnyV#!FVdBHmmZ}jH5fRbdvOa7)B8I|}znW$0B2Bz;f2cXW6(I{!0f&xF
z3W|z2!gVw at c^Q&JKcshuDjgTenP0!AYndHQ0SMqwG1zwP?O$3!Z`bSO9En{zy1I})
zZ~OaixisbmkpV0Si2>i(b}FhsgY3Y}%uGB}+z<l!BNrvJ!vGVwjDJ!K)RE7w#(nmX
zIR^(VQR^BG6b}94s0;2Dh>vHZ- at X;TbhvlHe*zL14BPPgoDmlZyRITi)MH#(Uc!AX
zV5BH60ih7x-Yx6dGew*3oa_l;dOA8fl|YIifR+l{Dl2dE^76_l3w{gvJ`Wf<;sVqi
znV+v+oF2eyCM@#cnM&*E=?z3XzOKTZIrs0=V&e&FJMP`PcQ8zG-80v`_k59ERcp#=
zEI0$k#Rf>6{Nv^29Sl?AOL+E-6bq%`Sd_8qdOxM{yGL4*&WrC6)zQ?XLCS<!%c#t)
z=J|yWf=+|VB^EUG{~)Nm8VdTL2$;EfqR5q{x3}m3I at Z)EAQBzkHIP<<`oUerj=9c-
zryzApEp$8U=bH!IsSh?#2XlvfioJZIcr}e<$!n|xQUV0PEu^HfPtQ~W#eraX<lsS$
z_2MT4Z=9?|5DL)(8&_y{?W!T?mM5BnvBJ+wDfI^CaR0#rO(P at 9^XJcRAtS>&IqsPQ
zL&c@@amTOq<IM#C|Bn?uJ|(@IHKS8I#!)w%S3i$%_3Bmh0XSN6YisNOTwWN<QD9<$
z+2 at Pjw6xIRbY1eP2S8lmxEr&8=)tp5Lk!QgC5iz&R8`%9qb%AIrQu^^HoPXU_o6_u
z|4y8a6SSz^u?8?hm39BIY8xvB1qCc`LnaVmq683)I!fEv`ygqG+I5IIE{wIGo_t57
z!mxxR;<KLR1pu6O<J4qoYAPIQR2)z%+?E8vxS&7;;(*HS3xKM!va(RIDMjmC>fy4}
zk^D|d>!+FMbBKp$O=-mIw-ZuK$T;AZb;xLe#<)vyjG4Mfupr38Hc|jchxqve!^3I(
zcVf{DFxGmEf2y0=lB&U0!bw+n3gA1A{!Uksu_1i{+YuRRj&Dw%UAGsOcL5ZPd2SO1
zNOgI5C~OLunx&U`<2=U>yG(YUoAe;6@*BSNO-R at W;mGjl9V_rncpDFeSe75`+{}E`
z;cd(@XWWzxK$iq}DC}in0Z>5X&f5U>%A_4RMt+J(;Za4ROq9R{%>;M%4G!v9ce-7^
zEGQ)<H5jI9e at 8b{;mnzPv+lUd7}9a{QO8F at yINu;eO6u(gN?4mLm-`*al&$9bv4QM
zfX$mWZCa0?FPA3EaPH3CW#fj=-$(sS{)-pY09bLS;@9$Py)z|v!Gv?^Uh`k9^NNZb
zR`TSD4!Bk0=ht$aW}RO8S-}aXV{B~f at prmz8gOKFZ!cSYeLVm<YfDS?%2-Kv-0~<Z
zD~lTDkVUz)6Qk2|a%%B%A9o8ta7P0ayf*)Xboso-1_#%(v*XpxH4cSnESuVf^`6Io
zV{l6|1qDUPfF|8(H8llb1=z*xS4F at bDRme@I*hNW4Y_O~;O=#+#;5>@dDsbsFin49
z{~yMGS*nosz<~qn*Hd)B^I~K9_$L1h7uc(RPdG2Md&gxL7x`1CZWX8{wGc0LbJ;Y8
z+`c`oTANcYz{|UZfPvMfHYfJ}#PBc~_~w%b>pxUeyVPTv10e!x9s<8w{OPlL8~1jA
z8Tp5YYnC at Z5xGPQDI0H;v78RCf-fd2xbr^byY$0LeN}$IFDrdFZKI~HLsiG~-qeQ$
z1e741#@0?))P+@<3Zkl<O=nK*%O0TF44&5V+1e0+ at RuswX7~od2NhOvhIb84gs!J0
zgK)@sc0HxLgoK2U1O1qo5V9d2!kU;z-wm?0%SCw`4xg3Z2T_rs+?bkxTa+P7g#ku6
z|J9V`@y^YX?vrnW*t~<~f~aukseZvln$4Rx52D%r<DzN1a6CdcGvb;3K`t&1 at C<+{
zaF`F7ifyBdf0mBtF!QUYgbhRLs|8YsNfh21BLI#FN!k|IjEoFO{+g-mlE;s;tW|da
z*exzCaqio9qV#Ztiukc(^)WFUk91!>wHo}=ALXV|D=#vV5oMRH<F1m<&(F7d%Wgv*
zt*u+P;x=sXcGRK(bd&F(C{OSOZiH+RD=(-|F!=p at RZY!SfbV$nAD_y78yWFXsylS}
zaJbDMynTSwpsr5kzY}tbQtYz{$Ux$fl1E$0IBm*tvjz)GD4hP&w6tw_i7#r)U}$9Y
z{a^aZHuUzE#))`T at p~7}IRbf_)#vXOtTAI{Tvb(dFihEf1RL-n^RWBl$hWcAv;wa|
zf%2q23%vD86qbTRLv>T^2tegpY}=Z=DF5HRW^m_YaCU&MMOP(-h3P!YS;NA at fDvT0
zy#&ba?!3u{tk7fH9ChUgYpb#sdVMMyZl!T%=Hw^>sz5#w7soGZH9R<YB6qDL;q~ja
zc~dS+vMpN at Ap86mado)>MWA-nr(6 at PYpKSF(;yN8H~~SQXyY2Y913{S?z1571~V#k
z2|p}KQ~w>3EDF8PPjm&ztp24-Pfy1R#KnuaN;&Xbb&y<&mgqH>ayUk&P(_>0u)BBb
zz<V|kCMPFRNf*BH&6_t6N5&0(Z>ANtjL%WV>7)7&xSMJH{R5C3v3qv9(>C|%`C^C0
znEUId(wQV|-GovzpI=_#gwKb&th^R4<ED9B|6W*F*iADk#s~ZjyG{IeV%`Kt`W{9!
z19V~mxC}`Xj<TR|L+H`3CrnKfu->!fzi@*0p~|ODrMJC|jf<lK{{h2=l+7tFu5Z&l
zKQnW4jh3h1faDH*{EU{?&5)4oDAWic5+Z%(3ljx}t=v33DHUuo-g}*g8g4{H?85eA
z`{Lr_pj)@dfi>b9Gf7EFU>u?zCwH|p>YP1`dWHbzlr=Pzbafe8TU${&<<d9fc<Jkq
zDos+^yFPrlJ-lzDZ%qY|5b^~`lY at hUeXo}$QM3tf1jfe3B$lF{Jb4OC1rLdonw3|(
zzso{!_ZdS&l!iuWm4X6#iCFc{6~_w~E_9Up5FmL5Jb7|1)*csLzIZ`G`26{EC0GrZ
zouIJrgCe>@QTR at Amyw<L!mlsQvkM8)$Zi8fWIokbg>sXV<qKbRuY{SDIBg{-C&wEY
zFE70IM`E>`nYp^IPRXX5ijp!wCFLfBgYNKA23$G$d*%WzgHKN0;#qFo=^(h9kf472
zD}WgQEa+Sk?voI+lTrN*FoU4&r>y8n_tDk^2syAQ;_51zn3yQ&Jfx}~wqej*xI7Q1
zU{d3~Wg+)8G&WB6`BC?WE3sAu({L_IUSt+=9n-<p at EY;bcuksPT+V%5S4YRcQ|z73
zr!spxJ5vh_ENne>9&XCZ&ksLv?Ys`yCYTf{o6lb%6thIt;JFTyx*qplKCB=F{KKCU
zIuOGF>^*I{m|Qynpa-xwyl3`9hm_#}wr{_A_b&BXnCO|r=9S^5SUF>3)*Qo<o4DN=
zZ1ttnccAIplVu{ore40(H*o2qMIkE-3lda^?(BS1Ur!ExcoK{)MFtLID}eaRuC4)@
znLMc)uBN+o?E+eRVtv(oGWi!E`m(^=8X6k<Mn}m}qj-I1C5lB(Por9P_w;(yNn?S0
ziT6~m7yBtJt*lT}d~VX(+<dcNl`l3DIaVyY-4=Z_I_HT}HH`d*l_{qqQ8Nm+myES%
zx~}|fUV_Ml($f$i($do}g~!b07Zg0?pRop$S5i`P86&RU;x079BisPS3_RsyiL><=
zC+)iX@$q*7s8IMU&oP=?SilJ;w2h)lIDiv22yrqizv5R5o)}me8^;mzuFsBW?&soy
zBicPQG;}6ir?mis`QqZ@)XgkD<<vIxD1f4J4i5aN`i(u45K;~uI&{L!>?!f#^)etP
z8|zvU^>U3-S02#V<qjivlobP>tv2hxq_ORWp2_uHSp4y=5cqtqH-H4A2Nu)87;j`}
z^I0`KzLA!8Xr=7Lx&btz;5fuYVet7I$FI%fZ7V7p<(XCQWM!o)G-#9vX at KB?yKH{^
zu#1tt&IAr2er=xD$=Nv^jsU1;Vsi3e*j)=Vvm3z8 at tzKM>~#0^q;wg%_iUAssi5k8
z7aJQJn)~|CcKjH|xplZw<B6~}>aVO72LBb9h&M2}95|q0)2$Av&%l5wBQp~Pnge7v
z6&g at RsBA4X2=(<Ps1IG-!j8M=?t%Nn#O%e-1;hhC7qHqZ^FPi3FH~MSudh$M<h^1o
zT#%ieEpoE@{rl~ZqFuR6NN~C3*xrhw`akg~Qx1SonOl&89d{cMT)CW0%G at ZyG&eR<
zs+>NZLOOHv=1qyl!Lw)2wvL}acdn|ZhXo~rqNAC+ySq`?*~8Nl<)`2f5puoodX;_w
z0<B8uXAG35Ps at Rg!<(f9)adN&jQ86e9gF~sH{^0(m6zv6H?3cv$rEF+%7H{n_XBDJ
z;H_w5^id>Y?k)}3I at a(+_+4Y80$2-vC+Z;r?8BZ(+^z-RVC4RnS at c-PSNx={t+c at 2
zv4WhGl%%tiUsUvn-**UrhS5aLn>U+3efrdKVR`srxItlC$tGn+idUX<+`wDlsmDcb
z>*?qicAQyhmVqP5ZnBMzj>cs#@Z=|o9eVou`)^M6lqCU<f_ENC^wKxcO=O1jdJ^6o
z6`S;(&!6>Cp(|A*J}^D~@MaPcA=63%00;uMFTf8N4C~tOzY|5*Ao|iUG6pR!dRzoj
zfA(zE*RQO=Zp_Tgwoy>XK?a6X9t`W|NBdxB--KSbu_wKuK^3Cb<HwJ&!=n7!!lS4(
zTiYeT|A7x4&`G$CVGHi)s{YEc<7I9cZuaG`v$A%e>VANc!1^XKG#->Rgp{AMdLBKC
ze*4ykDhD9vBf)i<=q&sq!wP_MV|mIBQj$A&?x2J<1Z{&(rOb9z>B0&lmJi?a<f1Cm
zU+E1>!IuNv7Dz at FYN6rQcCb@y1qM!W)2uqs?A<+fzL_N#C0#8oj`rt3fKv*s3vH>(
zT*q-%6TmcDT24+54)Q{R*#SHRs~UJG>*wcJFyHd^t1*-OY%@j7ryM_CUfwKc9tau^
z4mmp2&CSh0>ZyK!K|L2HDSdo=0Mg23>!QNn#OoX@!QE3`jumU&0q_!0NdjOhs;3Ur
z%mq*G{0HwO#ADTifG1%8GZz<@#sCaLf<=R>sF1no=a*97rIJEhR8-U$m7bg|ZzJyz
ziikQcE(~ap<%J8U)V{!<w5+TxQwy8FbY=>amXthIy32|U+s$#uQw|dwm#=JDiF32N
zaG^ap_Z(!ezkj^|8?(#Em~fj8egA%n;=V0&6*zH9puL8M#^RqpvgtZAn+e%xu3Qnq
zRwM+F77(@3F?P!0zAeZYTEaz;zi@@i=;#;o2X*VHEZAWPuSh5aUVTicZ?m$p%FD~E
z>@e{OJs9WFAGm}YZzLs|QKWx*pe|*Ub=4tCSV#yJyZ~IcKnPAh_PbZ at eN$6YcIl++
z!5yT2M8m~qt~vO~K)RK?&>>O8>?-bzDl01^zU%>Zj}G6}#SDn at 6~UA&GBPp<8 at NvX
z24D|p|AwC*2~KveEH7DnOgK4$dl{m<liEh_#l#q<UO`+<>bHrXpC8gj=aP at f)Po#7
zBco6fGOE72GAor&<$`eC5a6ZRv380ZH*RQI6lkTth49<n&zL6VKDielT5{ktwvnc$
zCIR+?(`l_CkFp5A3ZMl0TkX4wPOa850r+WdrUnoGT<o~kZI2_<@B=+23OfX?8g_pQ
z-N?S}%4%vgKPy(l(4>Q?SZ*5U1{q}6l2A+e;=X{s${Q>hMZ~>Wjp!+JKh3DuuwId!
zEFI_H81Ua7u-V(&8}E)%QogI1<d&9})^{*6?I0Z-8sY#VcInb3T<Gb56VR@%*!?aD
z(L-wXquiN8^lF}7C0IqC8*4{RFU*-JDF0`6)MN2m&@rR9v|Imb8o at c*+S-1Qz1BMc
zPAw=XC;<Ks1cWPMUp5Z_F26pE4MHp=793Ak`%_wsdVa{i)YTQ8oQ?nwz84X(72q{4
zzFS-*UXn#6cL1>f=`9=iicy0Sjpi;}Zrj^9$aO$_A3u45Z+zo^X!~~Aix<;=#QzNI
z1MJai`D1q0#nRFei#SL)SK`8qyL^$?v$3%qqTlY};i0U at abTlL61uX~yNYt;ls2s^
z2f>k1+Yki-;Y^W12 at 9v(j%+6qaY7j)U{0HAH+VBn!IUbSoId)a at WO!u2OwY6q-&=p
zKU?VRLj8#8eg%S$3E?ArM%4IU*Pr(C@&XJ~ch_Cg<@@_x>Fj0mE8Qi|cv|o;W#!~j
zPJMxQW_V&z0Dj`fiy~`{&&AC#$4CfMv$GG^FADbV?H*)Yu)7Vwa0)U#UN=+&>po*_
zOckLLaMF*|3~U?~nAh0qRXIfKPJ`PpVN?fE4*(5L%gB)JeSICrS?}x?!upZB%N at XK
zQuCiJfIB;uufG1XxQK at biKG_G1@Q8x`k6M;XGX-MNB!s+xS-ClR~lkaRYSuLNG4cb
zztVes0i{;5V!IJ*!ms5Ot*|Yesl4^!UoU_GPzqG#c6C4wDLKXmHh40r1=V?jLqltT
zG9vbY1dXDnI1`RL3;X)^xf4l+ckjST2lAauZCsC|qb(pC0JB@`Oq>qxWckP4H2nZt
zszenTUGw?&W-GxrC at 5&n9BXP)TIxQI&$Vto4G<KkK$?#)7~bZ62|xK=L&IC(f+;)_
zRu?Xu+_nj)A0kvPnPblmn0|GQVyLCy?#nZSr!KBFK`Cp?05jMMOc|^e$0~M<U*3me
z?qFr!cO2Uyc=YathqH at Xbck8SOS)5`OghQ`mSLcKF5t5G)yVQ|cRx(8tP)mAGtZ|S
za=EN55$}30-QWCydA{1)+6J at WMk^~Zx3{WRR)J4m2spiay<hi!j3mv0Wd9J$kTau~
z+5Rl&D=j!)uaR}{Fe;C`Tetl9=XH~gxvNHB+!w05hOd`5zO0H^_t&Txc-8BIB at l@`
zO!y<^_#->YuHX_i$TA^TeNiUo4GhE<iBz7YWYnxEd90UXc+$%1S$_v+nv9H_j~+3A
zZ}trhk>Ya5($Y78S|Gs4Dy4dI^YY5u*ktsxKgFy+H{P*{09hc!svq7bBGN{;&E?OR
zfiH<W58dgHh>5ua_~xXX90^dDkW*ddo^z1%<ZeIqGBh?03l83f^{pUkwm|rmfB*hP
z**<I~R8&-yRq`?e6p5;8iPKUC50XJ7toxjAfl}QGkieT=%OMLQey^{uzo(L-;RSh~
z3OopGH~~ODr7QEgLCc4Z0hPs8)i4&xK%962!t#+w1e;>?x_%uu!9c(uW=Iz6w70jT
zq%U?NPfcCMW_9caUteQ}TY^QRCH{Q>5Q!U<fK(2=y+xOnlcRXz1Oc9jX7}#2+4^yK
z7DYgieSLjXhQpA=DDhI`5#RZHk&$EoN$@7dqV|s2a*Xn=f4E&<;5&T4!e{pJGCr&j
zm<Yb`K|e(mO^l6gcXs||Z7cxs95N<>5U#uhzO1dSaW^o847;hn at W!xv9a}k2&>hmp
z$F??XZRxKeZ+^efS49H20|$O^VT!VHCB%r9*VnJ%x(di5kOTcwQc^x at o>lkuer=vK
zgDekX7cK_^yu6iAaH&nP#n;~2n(W}ggP*&)lFk=9*!gboZ!5euLG5P|QBgBHyB+rS
z_GoxuDM-E4)u~Ww_s*RiQXW$T8yg$MJOBhd=f<|8S-pIjpq;8N+q?a^pAL5oE+&nN
z%D6bCrlzJ~X2wn=5>0`+I66AkkPs#&CIZ5``|H0uSz23n{8?7f&``p*-~<5cmz|xp
zMJ`m#yyrpTFmcyk at b(G~)Vfgd;@r7CE-o%;$KdNoF{-Oc;GOJ}aPUJNYwwlc*8Twj
z-vFv$VFP=pD=RCl%IYQHyt<0)G45Xe`StjYz2c~Ho7GV;4#vE;Y6+}>JzP1?Ge&to
z?p4dm%3_j`Ua#1M?*nQ at PuX?l$`xF3GyW+X(V;?{Tdr}r!Q1M|wad<)QdjJ_aQOO)
z9L at qSsyLOx{PBu!`e9B%0X at oy0~Vs8qa&fDq(og)AhnKDWF4pW)8hIkY=awWj1CH*
zU|bnL2I&NM<YLUkT}TibwWJDg9XVyx-Qcm>jXOyWo68G7dr>{eR;>r at DbSD~cbR<~
z8q&2LD+&z_HFx at nJKQd{CL6Sciin8pdr*%}C>TX+Yik8!vH|=H2?>RCZ0rlbeL5th
zJA5IP1Hl$QrEls~vNuw|L=g)@{r$L^6tEn2aTInKS0|kG6TIAaB1I;ACZ1K=YuoYT
z<%;Kj#v|B;oDXIKiH4n%Q%>mA7({7;FTBUYT;^R}UDfsV<jBE9MR)BN-Z<l(K(MaN
zj%db6xNgU?hl<K}ZXO=UQ8&JpU#kWti5rMZyBh|Fhk3^Ck&}~$kP(=enBIK&Kym%g
z><!4rKsTP|<nX(6Ta5#71gwO at IJi}?pg;t-(xY2^{OB7S%YvO&KYnCAzUcr=%H;=x
z26qH5W7&W=*aSM-df=Vq5o|Zd_y%!heJw=J!zjB;mk#4nwwW{aIIi{O`30<{0 at J`g
zG4qDULKh)Z`{kx&?58D9OiaXuu(;d|021C;ugPZ~>%sU15JN#-y-Aq4U{k``OkG^u
zted6(G;47q$lWaUUY5WB_wuDUF6k<E><xr~*nct{RhDLEXzfg%967LmziCa#F0_ at 8
z5CpIQJWw0z2e1BP%NA`z!#lvgaj!0l#$z)j>LcwIwIQC?plNPus(lyEc?Gg9JgT^)
zBqtwVc9R7vi9<9*pbO>}V91Yk__Zz&Kq*;%bfQQ8%h*YW`1sDMo=$&KY9xH)q at Erl
zm>`gtIxwayi&JL6O^;up%-7!m0RXQ;*}SGsmd7ie)6%;6?wvADWCe$W+*IX~i<iDm
z-}oAy&F;PK6)-r|e*#PauHpv-I4v_1_iX`Wd~=Uo?&C8x=9DZWK>N6TZ;Y6Z9tq+3
z;d82~1;tN1dO3%T!RY`BR)#Vk-4~(4PJ0UrMOb?S4h>cpVp7piO|e0KqB6$DY_Mxr
zLV^jmZK$gH8}bK1OhfT42vxW$Gu<cdU^{2z2b6O_>jc}wZGBG8&KP$wl&mjUlY`G4
z5Z1Xr;^D(XO!>&Bu at KRkavEYMfiN;YUft3{gZqkHT?3+{nIRu1q@*0U?39sv|2}Pv
z95vY8y at wC|CEX`aY-Cr;cetydu<-im&=}Y<kJw+JilXn1#{f3LF$h3aINpn0w1BJm
zR(@{A at +Phcf+&DneQ`^n``4>V)6OMV21s$RqhjUHxCwK?YWmBUyZ`+8^FCxFV<jH}
z-bhcqJ at oEfe1A`Y6p at jU)%eNcpqRCF>O-<t(LgN20Y_G_v*VRIejHbAA{>p1ijq~*
zJ`Cvy#e*Rt<D@!<G_*v0Uhhd;U{jRZ&s1;7b7&GR<vD0j7d6eIL&OR|9`;3pXJVb<
ztM`)q7BaHpv>;mAbf$l17H*2I-f?lpwqv*#{EUvyY{M^1CYLVVg8%=fqocUV0-O!P
zVV>)_?%TI-2?X4ZLr?EtS$V>N!KR;8%ENbgX~6=>HEKO8 at 4pE|9i@;UH!yO$eER<5
z%ZqK~<W*l at SZ^ZW;^aKDYU8meutX#k at 87?{IP&Dlt%e3woMl=Yw1v!wCiUqP1DF)5
z6o-aV{`m1DAv=4yb>Uy#>S07=BrcO&UH<8V9)Zh;hK7c4`5-`$>9NdQCl#^d>;Q#O
zNy}wqXBg}85WpLW;Cb7&ZL6I6R*j3}pK at lzK-dL-M9eU`04xdq!kTy;ydm*p$EvET
zHUX3hN=o8X1^cB`R|$`f&b-(O7NocyMW5#8Qfp{vARCU0i<_F7@|dK2dh+B+3>*7s
zZ{e&2Iw&bAsldZu5Snm<Q{J@&2S8DML2TY0W4)XOmbj0}z4v+$-}xfcisI4D^3Bf|
z>hA77bM|aNs{LUi#MaVMiHyulcJR8$51R;>(xjJX+iG8JT~AjW5toOXSa2(cv^49f
zQ>U;%`ToNPbR#SXeE4u$Q}WU~_Wig`9=ul&aFr)@+3%yHqoX+GNV6^!|E=ujYYU?k
zR at _CPaGSvl%nYv^82AkQHIo$E$GgFu{Qdn+;Z5$mC0}TBWNd6~5s+U?y7obM)2X@^
z^T9Nnc=aTm at JLzVKVGa8sLF+k(Qsg^bNY~^l~c6Pz5$X1q^BSDtw@#1I$ul`EoLtZ
za0UxjeSM~=(T(dIpF7^zGBwh|DX*xA!-^Q$!oz8>lyK?+7qG1#0XgS%bo`Gw51F<-
zQ#+%l7o@`- at a!2Ew%Rtt%TyS5V0Bej_Y>WNd|_c>yig|~&nz!5Pux>Bbiu}^v;Sb}
z&h=!ujCajQscbv=acBxqHS$NiZbqttGN8dkfB5*=-v^Nm{>n?Q-$xo68&SXlFWevu
zsWhX&Ua&mJ+B#s{<G1BGs^HM+sp%Ttzn4{1B*lA;{(f`930z4?y%Un2KbRPVEmJG2
zS|(izu5jhclikO#4R#%kzI2;lB2?Dal0wu9c?J-3*jie at t+kc5BiDqC5L{!9SW at Qi
z6v;EyCt5#!>cCX;uM8-n>Ng-g9F?4^Wp)(#a_em90ZvX59A>^jLN;dC0P#X}$BrGQ
zfKVVIzWMxlH_!<|5fT3zH#Xrvx3t_sM at NTBxxSM7)-6qoPgL|lR8UV1oPdA;HJHF<
zC#Rdm#l?wM=Jxi>ZwhatG#r37Tm=&)YAa}3>C4z;sH+=@`@SKo1GgpfUi=mmS!#Sb
zB0Bo^Rtg4ub9m0W*#=vgnWIx#edd3hLklT(=((xGeZAk=)zwv8LINAtrsn5$MXWkh
zKXGxiSN(%)T6 at 5fFT1&UKXpE+7Uds()Uh929E}%64!}^RPu<{aXpBCJ;#Igtee2e(
z7zY?lyf!&H{`8C$h)}_95A1AH0u$Izd#hg<_=Aj$46v6D3G9B{vu97CSRODq4tQ*!
zKRa*NV27EXpP#uzk)I!lot at oBYy>a&*=Ud*8tN`_UYscI&9iQ1MIks~x)4&ZR|7IX
z+0y|{VFXctve&jd0m_CrhbR)kMqpqdWs$n8t0)z#RPfJ=RaP*A#i3}&DfoF}pXcHg
zIl?hop<*Jy4WI<ODw^u+HHnp~YHGV0KLaX4_3-R$zEZb|jM+OxDS8G5Zl<CK0KtYD
z_DFdx?(FRB1WchP;&9E=lbb6CFV-NPXozgfOi#ziSFyUXR3Y7vke0R$!o^8QO<1SH
zE at 1eZDrL<>id+xA;Yt;K{oMwIHUYqg%=;>DfWe)})Xnw}Q`(5tgWrdTaTmiznQJ3(
zrlumt!#yy-gi&PxfZmvEBbGW29M}q0b+6tMPYMe=Jb^vL4zMaAw;%fW$!|08dFBKS
zY4*_4(cv0p!xE>hzlNLZAO_ir4gg8OTh2Gb at jg`H$~xA5ZlPdT0GE@#t{U$Q7yOS)
za&PPy=CC|+E;hbHwKMID$RPkV0EDDGXSWghMn=|Fj`)#L2bPw44)}9L?0-;yd8+Ru
zuBE}%YPb$1w_=59`On<7v14_2+3J7<A}M_LeMaJLOIurY%rWPCkl+xqTUqr3_6liU
zIY!w9*$v-NL4nuM!`&MpJR|%0bJ}m;zUdd*?1BeF at y~@`A2v?KJGdtC<8;~+05E%T
zRVZH30Kb5jge%g3B+k9*c&NM|7(W at tYRINFwe|Pz?aVSRFFolb1C+9)qy&dxFbb)C
zMw at ADJZm>AEGXz35wQyx8J1&gZEY2Vi^9Ux{VdrtAkBn_hihhDSpt{^uL4~O*{Fqu
zMb^0&&zgRTu5q3zAlCzRY(DZpEaJq6{{H at j?<^rFCoC^7FY?c-&1s;<$8lg3_f6vh
zN?KYDm;D<KVufQ7NIt<Pn&OV%e)y0cQt=7yh(z#-!l<J4y<fWHUQbj>Sy)(fbalDE
z(%r5(qpe-7XL3E at +zYFdr%#VR*@NNEvYyuK_xDJT<+%>juzqS$7xw;T55p<$2y^Uc
zo%tZ}ZFm?{EG~h6={!vJ_3ALoUP-qf>DTC2J0KcjJ>|ebuEfay7gTVH6nu{=Uq9>O
zTU|Xp6mT`QwWVNYX09QXCFmE~?ZHmPO(di^4H0<v?%jLSrxaL#Q=mEGT1?YQ!rwpR
zxtA|rKI7$$(yuf12k|l)U{>6pd%jFqyDu+4{$paoR at CB!@`h#3Iss=x;V$e)q-K>u
zyn at ZkvlCr<BNvIAu}`-yj0JayX=!PFd||DDav}Ka<JUu<KR<#I?&;H~_?UnUvqRl+
zz4`0wOJlCtkaq?L2lpL4$_O^`60ob2lM{Vz6dCzu1RcD5d}j9c*=ebtUd_(Ujjzh>
z)4B;sAKPl#**77uxc)~37-`y~<c(6uJMaZLmQC2I;fxeLc<QP=76<_i=j90&IQG8I
zcS!nzLK)ODe|_;<7TXpDD*kHOwyr}^8k)m(K=#(k8J)N%_lU4C`-6H at Q`3Zt-EQvg
zK|njP&HPmu03g6%Q*g{2f`WNX77#-)<?a=~On at iF5s%-)J_k;zs>)x!EC{EI!(O;p
z4fuX at R=_gKgA029T}+i8yceuP^6$<``NEpfO!aNDv$x+BP1 at MdV57#)FCb8Nb1TJE
zn}!T_O+l1ECbb2%@*xUM*B^|>=C{YkuZdYT$Aya8e(D3@<fp@(t at llPKdsr%ujRYN
z?DRSJb8XZT_5g9mX3Djp5+0sv_*~d2NR}A>9y>{aDq{l)uyKo)M1_%#++OXw0^Hnh
z-be7{d;NBL_~?=J+*4lw7RqXBTc=jlKCgPUtJbdca{W+`m8c>TQJ<>N7&0!duPi7m
zWS%EGkt`1}yQ(Qxa&_+8lgi%cJAAynD5jvipB5LIASl<6S>EF6I;W`I<%Fk*(F9^4
zbK&~Q1aHwYF!=NK?L<}<FTv-h#WjQD3TkSU0NuU4y&*(Jm>;5=au;5Vc6 at z{NYwe!
zRg7(tp-uvP=T4s71SBRrGLo$CCsSl#AgON!+v9_g9{|0^U!^QDZ2Ge!eEYxoBO1{?
z_4V~QZ-&b|0w@^GI}0v>hto+gh_b_P!#(GyV*<fdX5lw5A`}(F6+H)%IX8~?D%D<b
z-#x-s36P}O+1VdUy7L2e at efnbNgW)t#9=k1u)S4%Resn$kG+l4R~;Pqu}2tKjx2Y?
z%6P2=gcod?7ZCV)i|a>J?RaTaeqlkuL$|x~p^fV}%V+Wz5G=%<z~Xq=cWuHe+OHl1
zlnpV*=z4ut%RgS(puXEGla%fXUTWQs<<*Rgu_tvQHRjl8HUHxaemG2)ReofgfW#oM
z1I2MnCi0y(Hf9B1#<@<s7G%3VOM_e0!CiH9bP`^@+FMgogU!+B&h44(t+)%vfkUeR
zV+g(wHgOuPPm(<`)$G4xU8~9gq`8Zo8nzud3Tq@<UJars12(<db-$$1N~ecR8<HAg
zbMfK{BO{ixXV0c(T5{0d!v2zxk&)VsUU_+97T_0Lowu=F3- at unf!98wFF)rr_>RVF
zwngcs>o|jMmj0%rM~`A>7(AVM{%+b^$;KU;5U?Q;#$Bwnq7y5m2T}^a-afWazi%~l
z`>Fl3 at 4h<`e8JNp=(VJrW<-iH)bQjYShPj0gJxn<60U8+jy?$8yCkSKBF82~4RB;}
zORuD_|H0ukEFR$Q`FnTbQZh2O0}n$rX(y*6v$M1QVe;~n|I`}flV@;cV9jmD0lbw4
zU)OQ+EK=UT**DdJ%Xa%7oo}p64-EP1US0i#+8#JMw>b3;fEM-<;x%x1IPSE8H$zyw
z)@IkJSovs1cq5H#1!6NEF-`N(;lovUae1`YOZ%ggsAxD7oVDPQBY>5U;)oewABu-R
zeiL2U0CokGlpK?ilG?d<?+!LLwv$Ih>kL#B6t-gB5;eyS3=9ql2#`%J$PrfUZ_dl$
ztpWTfEnQWP2)F*X<co`oQq#R5ZWEnV at 855yqN2)s{`_V at z*bD{7cK+{S=9EeEH9yK
z4;Wv2-;<~h?y_<Q1s7>>`m3No#ATxM?1=bAeT*;aKe`rwH3mq%K5w8Qf9}~_Ofo3H
zdC}JPs7KL|A+^IHc$cV4URWqPKiR|MvX{eV- at i_Ij)?ghMibNDGvBeD?53YzK-e#x
zj5DT|59V*<>O$0a#OXT<Mt-mUU%0((tUVLY7#Pyb%nXiXjf{^|+<Ua{RcV)QhlIQV
ze%UuLfI}=mR`f at dHADuNAL#RL$eN at Q3z-vHe`PJD(d^ubyFU~&Z43N8wG9pT)`l_%
ziP?9WjuF$bWQR%=zlPL+q}9;(r5^KHkuo$gVipn#AH$;Ko9b#aM at MQN9v*!IgE!#+
z at P@K~EtX$f=*O9BFn+9jrC8eV_mWelmzFX?OvI{CkwXu+%f<rIL<YW%yNDH52 at Zza
z$lV{*69^E(1cimQiRW?QD-h>9Y%;8CE8G|RNK!6nw|53+V8Ok+rw5hXcd$t4z>6Ug
z{)bv^fh^!2BZww`m6a!+4x~vj95XSLm)~;p=FL3kVRcjn{_kZrYQe+fZb!&m0|1Eu
z$(DW$CrF*s4zIVwVjjS`og4Ijae5&1xtpBvX#n;BRq@|KQbHwXj6o=t<}|2W9#zUL
zq3!%{uQ?7f0GJQ@`_~KW=?B$gPQbf({^G@^zdeKJ&Yn#XUjSI36LVT#<KTn(J^;1A
z?QzM8%K6k1uY)25w;2UcU<)^)KX~xqzaVOm1t{0z at 8Wdcix+b6#0X0~reXoEmb#42
zygh~-c6|kWGO;v|yF*N;F;U?*)~S+HQ{~_foWSh;|DD<>JV{<*VGU3+OyJvCQcszN
z|N4$d9zKDhl@%B6OZxBSS$zjwfAjWjim#sIe1M}3M#KxHvY*X#nD`*HgxG{TQ<z-s
zFBtNOitdt at miCw(p#i(0p{Jj1wS8^(Y$!P|Zzos`Zm4vd?Ed|B7 at ndUFU0>YUcoK6
zk^DwCRk?7kL(uHamQA#DqI}qaPE6On0+xxlNteb&B7bhVu#Jie?;kuoUVha4?{qRU
zxC?CBGrrdKmN=6Q4GqCR^o@^?r+tv_Z7&yr at 9TWA1Gcc^bPGI|=P4UmvJDEK at u9fa
zn}Upt3{^2d?u9HMvph at ZIsfApc*qG}h9u|VCTt|Iv&-ri4_BUjtIcKm_1fYTJG$)1
z=x9}CCE at fV-c3^rwT<vLN5;n9z(4sl)OaoN#5rf&*QNScaFeH}=f$>Xl-SA*h?6+Z
zT4divYGPsnPAv!dT~}8ZQ~4A?TZs<Ouh()jG7t%2?@OtxEfzF#a&mA36MMMwn4uhZ
zL_Z2C!J&Q3ILq_7tIMM8nc7?_+l42hwhREDajJNGl;1|IIQ9nsp_zq+pNY@%*^xtY
zx*hW2xK9n|Ero<`K)?v1V!<T$`0-<dy7ttZndb2hqUu<NP<wIFFGu(gLe@=t36)_i
zl5vZFui7dL802PX at Ezvor{W0T7AxT at d-dvXx(*zKymgBla6#DKV>5;_WEX@)MC2_k
z_pi^kt{jy9`SB?UHh*9E at Q9s{j}nr2a9G%oyg{O8C$NCcPg1rq{_6$Uy!l~g!ZL5;
zgyc5`tbLGCv+5W;kl4s}%VootK9c$#zczou#Dp!kd_L+v?eTQ!!ht`Z&iYo^@}Te^
zE}SzmGQzWa{rYwAojW>v+lcdmHkU53A3C(HtJu-;;asqCiTHnYIdx4<8bGx)tgP7A
zur2CB|Jz%iffQ)V<o>pBHPX0|byWb+w(iNDi`V4N!P|j!Yg1kUky-W0k?*%`Av(?8
zYK-OL;=;zmrTK~RhBHP+p^$F&@$zOh{Rj&SL**Yx_Ty3&(hJ7t&S9%<jQEwrCJSss
zf_#3Gm!ZSJ0^ZB#QrB_1I>64%z<52zqTVym(A>a&^iSF6L*jWhj-fnecGl%(X(<OE
zpV|Rtt<_>KjtBL~uyOiLQ&SVA0D+PjkAE)B$#?&0jEYSsmdCg?%5)ot1<rS+pN8y$
zEv|*OdV}A;9}e>gcQsBno|7uv at 960Gwyw@%v^C|isI6}HfcNs89*!^J5ZC<&58^#5
zWEaOWbMt&wy_26klm4~vICv}<Fc>K*DYleDf{Q5Cb^fh>Ca|?tYc~@U2QM$GJ3edu
z#J&+!0tX<U9Xxn2%dkXi)V5rC;$@EQ-xoO?;mT9M5j(!Tq7<1pen;spYfa at 1lJNL=
zTs)eRt0e!v`qV$%;sj_2uMprEpC%o8wtac3ni^@I<&izOcoIUk=WrSPe=IQqM&#l0
z(})qLv9Ynyc(qy(YJhvkwX|rl?*I5PYCv|Ex>5jt(lA1bxQ&7WDFl)cFg_egM42am
zE7<I*aGRl=SS|pB6k~ju+XRl~0xC>MPDU-mj~yL#@V0=k%pA<QJ~Ma<@8+<z_0XX+
z^rCuM`kHs+(`L48*>Yk(ExUw-!N<M-5>!1Paip*jb{|(#qek~IF)`tIl8z4jTXP`>
zUi}1du6_GZjvr}Svesc7*T$5jr#ArbR&TFfeCd0c=yY4ADI4R;4;^k}<_->2eLsWt
zfAVPAc(=*OJqdZ1_4f_5&qG$v_n0;(VDWF-aG`fMMm4;~#Jx%q+cjT=$Hs=j*;QkM
z%qKhYUh?MCdU}kH9z6mh*B+DD_}s+HP4O~!S&w^)j780*vvd^aJO{%5_U+pQwP-Pf
z$sr*jsM5E%=z)xj5H-oF2+;VQ<&jSY!BLyk|3gW_*XHf^%6J<dc-99Lqz{0~)WSkg
zY%B}*l?)7!@bF|B)E&3IKfk`DoRn~gm)Ac$yhXdWqrF|>)Tw&94sn-}Guh9kMn*1T
z%iGtltdRmHIl?&nuyOTeT<+QaJ{l~Fj*bp@(ZbJc`ZezS6G$Zr1g_<$Ee}xW5Tz$v
zUC-L7WS_&PjghY6pLAD&lKFvIw$Gh!xrmpYxE%x%0F4OQ#?+<rH~658jEq%mP4gnF
zlKFB#CISNkaUK>h2aa-bOG`QpZo^q5+%2S_pm0wmC8W8apa8qI`o7z!h#D6aX-C4j
z%PA?5b#``oK<+=C{flERV>tUj`OJGBywkurS4jPY#nr=|^YimBT}JP#sAq~DIr0X_
z?MB;DDOdv1&f$d*UIR^ilS@$#A&qKwzxf>3jzXOJoM+~z)$z|EBNs+mI3c02b8~NB
zSy at pr`FYi*ZZ11>J-|>RgOdFDN_)6e?JBiJw3z+W2Z0LHH~!?I&sk{>ll at mj_>MY;
zd;YJj>yD>-|NloulroA2rz8=jVI`4Bk&$fKqhw2=?2!{HgrW!`vyfRSyOebuSw;3W
zN+^_BzONUD-|vs>(I2;aa?bg at ->>m}t><goHe3-vR=fQD)8RYYgz08yXA8Qfr>Eb8
zd~e>+)?ocHQZf0Qj!yRUyt85d&;Ix1)>iE|#(4?9<7ulGrHtXRA;#5yrZ)LkI at KW%
zLPjmE7FRFNF++WQ4%8$;K~hM_+01?nlTuV-^kTehTJI|GkjdmUXAe&?)+09o<5!lK
zQ$5wrWc`0vGdOq?!p*j5uLQ+!`Vlx(e>!x>pSu1yaC;6mrM7md2S8qKVwFopB`hXp
zUVY?^Lh(0On=|nHI0uW_YH;CLJOwRy={YZkw3?3nRZ_o<NEhtvf*CgPrv3ZGAu+`g
zFwQbu(V4rk4WbOc+~$Sj{s95|oQah9$j$YIeWz?&mSNc4y98BL)#PlQ?)IOP&02ad
z?1LvJu7F87p`-KSAM==r9j8HDF}%*RsJBMfb+p-kBW0jqs`O59u;cWYVS+h<;Nj_s
z+kpU>AuSmWnM1_KOe6*aIBgr*zG(Y4t6_daa>}{A5!GDqC#rT=RILpg^B&e}jRuE6
zckk#$LHmqwfL~=8Rcu``O{XDdMlhCO{w9BRawrLqzOY3dPLb5q)~bQ4y>!yxWV6<i
z<a*BkrkZh=Ef8w6+KW?Hd3nNUk&V4(X_1|3wBrTt55{T3yUL50ItMe0e5!!yqJyK|
zGBXy6smPKgn~daSWT+&Zzdf09rhvWS0gx>j9#;GfS^4w<*KYzfJX1SPzcL7UeQi7u
zz?$D6S7Oxr)x71=uC6W|m;npqAF`%r-0V?kC=;ft3}3tSF3e66d at 2K&j_BQt;8Sg#
z>s}&HdJq#M{fkpxUY>Bm&8^5R`m|y8rP&wmFLH9I2n(}A at v4v9d&;N?KAD+zp7v^c
zHnX$i at T2G46pO?<ilF6T=htrekP`=@))NPkkr$5u(epYE4h~BzD?A%(!*3cI84qjJ
z+8R=JSsz=x7a56^t%S1S#w266*e&TgQVxP=;6XvnXb1~aO{%(Yt{*1!G^t9Hboc*_
zI>ufFDh{5&mx`K>9yI+kfH^>+Q+!&D&83 at +njvIhE8R)Cy(jIMXaNK#=B6^xZT-GM
zLnl3b%XoL`V)}?^i_{%jT3XPa;nvI;xPsw5Ijg8BBs&{t7ICgWAql%PajUg`XA$i~
z<(qwdX7JX86G^t$Gq6mLEYaLyT>ShDO{uDD2{d$c$A3MtYD%FZU>S39VMrP`kAv4j
zmwW#{$$6wT;o5kYsuaQ@;heoZJT$$%y;z|<e|{T;@5#ExTB+Mx04 at q*^F9bRDMXgy
zaBOj)5=;b6mimWGs55TZKt;f+A0~uAbFf1C<)x!4r+#;|dcElMXvg$hn&zm?(dK9F
zl#O3;ad8- at Aoh9zkxg=&YnWTh?dI^COglHaySpQ|)#Jwmu-r>a>)V*yL#6))swJIv
zoMTX<&VBy;1n4c!y94$RDH!*^Ng0c2ZJ;C#qwiua{6pC8;G)6OmXP?=EFqWgx&-gG
z(>R?zKRu2_E<r&-*C*?bP>FO(-NDL6Q`3#tuU{`+LQn?BRr#Y7fs)EJF5O}5`HKzV
z2gv^V$Rvv^rwoxz1oLgJj*AQCE{5Y$<MX29v~+abLPEK%KbNtn#)8K;3Gb>q+ps(x
z?;0xlesO&E*Q at E88bwA<@gG5{#;v#%JK<yiL=#U_Ef%`n*)lX8$GfD&j~+Gs+4s(w
z-GlN-!|wzE^W0_^tN{P3X}{)@rwwbj$K-~=hWN#|^GT<Jap{_kVN!``GT3x;h=No(
zHLij%k4oMyEtPip{)x)d^N-$m=?95}>e+5xdg{S;9fka2{Wy;a|9Za7ovi}Q`H%9o
zJ8^4s!=t at t<r*B|yW{3WWh3>-!HL+%jEL_e_n-H}{nI1I0w|`zA6I<iXgtW&DR5VS
zKs{|x(!&&Z=;`UNTPL|XJ3B{oR&L$8H4wzBS9)<a^kW-0_dp-dinTa{%%JJ%{8b^u
z^$(SKbwm99X|MtbK_zx|g at kOx$Pjy9etl~Rj>PTX$h6g7V6&D;BqG2D3o_G2_nU2>
zHkX(fyNrwsR*L|<<rft64i5){I_%=-|C&D3I at Bd9DvJFY*eCbTKmU+Fe)KCW6nhjK
z%d&mD-cX937EeSqw*P}U>hHYPy_)P(%M&)QV#K at o(Nq5N+ISq<(dYxtQaF_UVTUVc
zzWr`xx<XLDP}0%X(?$^|Ar%zpA*`Uw(Or1!Y?M-n6A7N4o=D7zc_6o`y@`p5ah7DS
zWuTZ?z++=$vs{+f(9u*;R%S%rv}C2IT$vaJ2HeL5XJhc{%BkVKgy^no6}92D1Bm^3
zl^>MRQO+}wmj~b~got>l+wJPTpAw~24NvW7$9@&*zcWdRDbJFUu=yDq3{EtCsk}bg
zE{bS**REZ%^1Bl0%2v^SE_Q_i#N>kTZKO5RCXB;-`$+Yjix)4FTUtnwk?i1x?iWR7
zgw+mU?jC;bhKE<E<Wnr5;shX|snL!tT3T9xw{G>tm&c at 4J>pTvPH_N2)JPxz4=2`M
z^oxz1{U(TSe^Ti2its&3{vjuuZG%CsgdQ at 2eUg>8a;&JRz(%~jxnCoov71)Yuu=h7
zz at D4>iUn*C3O*;!HApK=Fp0gL_ONGF%+9m3v*X|~7-Z}a0o%Whn0`Lv*|WEBLj(c<
zi#4fM2Fc0EOO#XY)ApR=mwjR?DOz|>K~<GWK7`Y^q~y5U)UYqO)AhulXrC`%4A4Ch
zd;o&bKT0Xg%@snDWh{OG&II=(;KY+?ft2>2O1-7Qi10bxV;=y=jBPJS<SJ%KsYHp&
zU|VT$@CII$=;k!0?k7cdokeV6VPPIUd}_iKtIpL596k1FVTpEhG#IR3!fw%HdU|@u
z>OwG?8g7K=kUV1l<pjjCj)QgE-oAT>CSO*b0)VW4gW<*vQFl0wYy`-1>hk47V8IVM
z?N?Xh4x_iQuz;AUtf%}2Hu^j9klwzP1A)acgA>=Ui-Xz8ejs#KuErE_7t($7zYiVi
zW)oorGyrjTSzR6DPOq7S)jZlTZJ-=C=K8S)l6dO2e at IhethDzIEN#L?i=Vrhwr#a&
zkS=-f;6Ze0``IfyJBb`(wmqAO9N;C92MwjA;rP7ru(%@51feH`@Q~cxj2U)GNlE{Z
zmF_Gv at b{*(69a)kK{6g5lAJ{1&ynm>T-iKq`NU5|L<Gm(p6S0h3u@#ObHu at H;@@gq
zR{tzB6PqTN@|U`Df>m&XJp%)zva%C^H`WnT&p*w|0=p=T%`r$MYnG!stU!_VDZlUE
zzhhS~QqF)*CXpTWp6t`f;yQGQ8R;wG*FdP{HYjM;8;(-Gr`)}$DD$x|uduuB&6_tk
zrB>e9s5Y$I?2ldD$Y%<{GLEv6)^Cy3*ViA`T}hE^J33-)>LY5?3XpMYx7WNN+D}o@
z<5%M}m5`A~P|qJ<{P2MuYr&s#%#TXNC=ej<8SXBXZf<U7=HOU=^5jVr9wcLdI6eoy
zJ^Ju6AF5ShNu+yj`&kHGH#z{!5hwKLZ%t1}_Gf0an=CB8t(p3u3xV627qufOZW#N^
zk&GG3i>|ImQDc~n&kz84vr24FfBEK(B!(<FS1QL{jufH*v2tW607$`Pw7sA*_hK#P
zRj_e6f>*`w#tmxZ;D9KQff!1E{``3?xndJB at t6yjh{$I6Kggx^`LPNQGc`3e2)zt%
z1dg75{`^^1-dbBgK;V$q!gZ8H_vx{VH^f%>dM2v*;!Or>?jwoRVBJINbWH)LCl&{V
zgjjLkW<bDNq at Jd;OSA}FDc&^y3ra?P=FA!095Y((-MdY#tb)+T966!`@@01A$`vG3
z#V{8vF2q8zva)Lo3=K&ud=+wwnEk<yGfcq(*u!efg9i_AsOzQ2Y%cj{8Y!&$!g5A+
zCy3Q$fWAmDi5Bko!jF^r)-8&|fpUUz2?@LU`ufO)cKS$uhkFJk)blQnv<l*07%&|;
zjuRgrPY$CfW126?-`|8%xu;{?>a(!m0Ro_;p1v7ZYht3Xr^f^vctNc;OR%%>Q>_G<
zX=l_mG%D}$Yv5!#Qq=(rex8$44)N&?+&e}LrO^bf1bfmu`l9<L*fDHlbNgkDeJ6jW
zy91)P*Y2V`e at 0DJ6`f7%v+_*+7tb&E%3?M;BqRh?P&>;Y2*MzGVpw!e-lYb!-`_u@
zW7T&kCc*Xhf>pw{&ANxe2~VHybCTnp!$KeKxdE?_{Rg;D3AAcG at oDusCS27bg*g!1
zklLbS%qGFQQuRLrMh^+{0Z*eh2Dpp6^(3_O!lI+sIXF1rMiqcM at 1jL*xY%!#z_ciG
zXAFAFP!40;-Q{){Bs2q;{OR*&)0%tyD1g}Vn9#4b8$iO2Lfg-N1vYKm!or)1-KVRX
z(>1VV0Dj=KC^uqs!r56A8}PyZVR@%9Ngh*(AnrI52B-;7;$?om2)1p%b{VaJ_rs+#
z*q}H+_8QKHSz-rEOVeO|%o5Konam81`bKtkHaRT%PR9!eT#^c(Q-+mTK=m>ZVy#+{
zW_k$ak*E@%7GkM#cv=$&hl5Vfc%SCyZ${_~_kqOf*wEqP;v)N=n|sgyRnCtg*#Nj)
zEEFL>SwjO8T*Fogr at d3Zevv}tH>ACMxfMXSJopWy4oFTWA)_6HeK)bx@^eKhsc;sR
zmzNHrNl+1*nwlD%Gsjd?clXY*v9YCcJa78Tj~~@hR46UA_FrC7DJdz at tX*sV?}xC<
zj*cu6+$_b9L8Q^Pfb?%YY(`VE_}3k`brF>=;;h_Iymu8iGI|gic%-=c(G`2z{5kIu
z3oEN1oHV*c<ebF`kDO?n#zU6PgoK2<ipdxc@}7M}5AfDWgxi{CrPi2e@$}g<M)*uz
z`hc5ZK3ovO{&6 at C94rHkBZVj&>;a?>N?H*o<&M^O!-fs;j3#dbS at 6gJtir!pK~CBb
zWWIbUE_dn_9t;?_Ln0z?eW<7pS(3SCW at aw06QUyUurlBbE~p&-o7A(heg+5R!Rcch
z?BhcKxn4(%2;5FC at tntQlnWOxngZ~`4glO%;I#ifMzX&~+AxhN4^|o at bR;VP&nhcV
zX>Q=AsvfukQRHGd#O*i>2so!Fui^NOSFc`?8#T3Yhl1 at VABduHatPaNF2#G&fdNiP
zVo}>|wjwG8^f>!?{RbBo4VUTBv9pDG?Z&rdzSOvG5L`9`G2+`f_)1_-5XT=rd>C_s
zPEJmngFj;r9GD35VG15oIC1poQKT1kauNacLBbboAjO#xfJbl$ph!%C at 5~aC!oxdD
zy>~J&Fa(B%zORnpL(&AW5S0K_u)BYHIsh#2Qh{DkPLb!nB3sNYhwhT#-pT-6eSzct
z<VKpSL;ny_Nx*2yoH at gQOGh9YyV7)sVozGQ!xWJ189Qunqc+$njLwmQRz*c+___en
zUX=d%%^~K{#TX=qTXnG2GXTTS#y|zasvNPWLV)`7=TC$#0P>KZJ3SpW at VdKuB|M#x
zk&$J0NikVX|4DRkJ`OB+&WQjNL`KkS*RG+=;V|RGgzQoeq#{M01QZ1BV at 46)`PS)9
zP!UTT8$?+4_VyH#l1Gmo>6%+sU?`7kYD7iZz{%snf&2S(kY|jKk8kR0)7q}CE*xZr
z*T{}R3Ix_5Odreu0>)##?&fB;F!OUM|Ahu1)d}D`ZZHAdi)*m*ZQ3?MkU`JD;0vm<
zs{ab4>I*^$JRrUc3=SrdNVM4E0bzf6YpeF~_24WpBiL!r0!A`PJ`@Y$2p|0!Hj%Ro
zk0tc8g!!x%S_O>&{Dg2G^#kqU;o(8<mb<kB&-3zbP-cDl`qrQg{Pt}f at x^(5LBV(L
zZf^-&W^d%-<!x<O7#SViuyf~5<Z-f%=A*!%Es;@C0k~j!*DfkN7$v1j_$J^Q03;KZ
zN8~W?U07I%tJwMc((Z!Yz$vMH`;^p_6jfAC85!+Z+N_+Fm38^*Rcz?$>FxCuvume+
z6dkBp4-$dQf;jLDhwAI=i|c+C78atlMvV_dVIMXeG`gIzuHxIE(PkC?44COmNl78I
zP?RDqsJgE1Ed)q85WXPgR!a*Td`sNYjCANulF9u0_sfCCR8&^tU?k`VBT-^A^7!!G
z-W0ZG#n%V^l?NdY4-d!vM&RDyT>8pytVLq!<m5feGgge3E?p8A7e~C0F!+;z3U^o!
zd^8sA0j(k_0?1K+RWJt}zhzq%5zijLbXT$a)!l+CZdePvAH<kOHtVkH>21dx7Xf4P
z%uGRBTU+dOJa+6D04W at 0JOm~|+;fsAOaU7wu!nG&n+X$*Lc+q>?)A!GV~#H4zJ2?!
z9qBMjXMKGz)Aa7dA!LUJvtSCI5o2xuzu3cpWAx<5GAdVCHBC(sNBscrzzvWM1n{zx
z(_28}SV=6yHlx55kIAU1ql3-FSSg1`L5rAW_}UEK2km^R*B=ucJgeKHZDfR`5O1rh
z`r!0Ik9?P>W|83};bg#)jeXC<%~rV**Co24-N2KJj7*ui<l-WBL1-I<Dkc^dbT&3N
za0rzEnzXgG4~dGN0vxTUtTqM#Gm at i#WaZ}N;T+5N at 3ts&M~-Y+?ySPq3CQMSXo%gs
z5OrR?v6XW4>HY|g8o3_bzfY>GQ`XehUPAyEguD+A9zH~RuL2{iGlT2WSC$%fMKU3H
zgAWZ2^7i%z0OQ^Th2S`1kDZ)2z^2SgO>i0}Q{m7|L4npAfUPE`rsV;9Sk-R0&HUJf
zL+ at b3`@k|T*N<4<6c?l6zwYXagt+9!Miz+-92$d1M^?+ZDJz`G(#bOLO-bSP at bJJI
zrTK at uyAHu8N(fLML-gv`uU{#C0J#xrY80_$X4z8y`0>N1xVTucweF(OHh>GhfCIUf
zCmOH^2mEKzwegMK-ri_7K|n~6k&$TDNUOQnVOlA#g{|OzZ=L=(+S{nujSV7cEzZ>J
zY^-D<WiApX1qCr6V+nXY0BqRbd&@iH>C^Ss<yFJ0FYFZ%r~qV#!>LHB1%Qo~mKKw;
z_TXh85c+p$2uFc$P2Rb4$Hd$`$<p8j+}i#7Y_ at iG37`l)M3$9Ihcv|4_#1<R+;9dM
z;=|SBRm8;HhA<WbT*o(ASqJdT0K#$l{)q*y&<~;ncyF8{L<2lGHwWq%+t+4oZM~+e
zcQXCiGdV}coW5VSr5_du1X?ES1?VZKMxNcjf97AhPai;jgEnLDUO*dI>jev>KQfe*
znz~j)Lj&0#zI-wGIReTH|G`O=aAN*37MPYCUORx3nqb#hz>1(xmzLg%qs2##;20jB
zJjnGs?^2|$2~&7-Pm*cpaU=o62a1gizP;t(Ut{;_$%v}$B*z7oZFKv0mokc(4?nl@
zYggIQp8t0?IOtd+*nMxS;e4*XL*k6S-wZ`2BN?ta#u#PWjZa71ePhn!D!iVZZ|JD}
z`}gm^IZ52=*A3~nbZ}5zyLPSH&py)c>1prko0kaq^J9_uR2V>vsfht4B9Zv@$B%P=
zyo?_zpRTvi04ro&py#$QlOx%nk<@rDsqwB|$2FJKjt>3V5{GXy02s7Vn;O4<O$QN4
z77w}DQ5bjJGviW2+}GL3q4tiBh>@aeE_?*<9kT69ANIwUi{8n_#bQler~MJ<F_+it
z{7k;Ow^(a&aBy7VluuSaLz0z&;}M9eP0!;QxbiA_lz3lp&o4>NjT#MYZEY#LT*Nu8
zg&tZ8Jp at mtsJ5B^7I*OJ-6YdAuhP;f&#(H!Uz`mg2Dh at YQ35qrjFoC=f{RxzmR|aJ
zpYCd~jUL at m<MVS1vw3rWN{TwO+=}B$JZ8DOr|VrE$(@Pn=@(=>UKyMX=}$YicW-<{
ztt038$mA=p9%V3Of{eA8f-O at 1v-faJy~vKxsIfP?@!K~B3 at -9U1oerxT8ezM71Lm*
zxz+#4KbJ3}`r{wLTJ-`wtzx67k)rl5Uqps at p8fsNd-7G*Yt70vKE^Gxv#;7m3qwLf
zTkKyrU%8Ta#G&hf#A~~b3CRY=-n16$4EUq{G}li5otavAeqkYRN1;Y(ann~#cyz|f
z=Eu$DCXXj4uWG~_pFgjb)JXpQyD(1DHL<hPaCB at 8ewja}zN_Ij)zZ_?tOd)R8WFK6
zHZCrs>1$fk*I_`xMI|MXQ<-C7j1nr=0 at d#wIR_?P^EMBHYSs)~2``yG<@IMyE2&Yz
zk#l5X;v?fG6}npoHtN<}<V{Gbc64+MHdz<s=jH9L%^m9<CCq3rf270D>WQnf$kS|U
zF*$TDyRGTnJ5p7D4j=&KKnW|a;KUCim-p1?20iI{Y3b=HRS at f*WBHJ#bUn#W;QlQJ
z-zddBM?;eIGE?AY+NTVkw(M0{v-AK9Pf9yvi)SQb&x-B{Wjhs?(v<GUMfNzZ!%V~5
z%*XG1wr=Pu$@IaTK5v9tx~5){{3`=74wKq}-2JtRq9uYiD<@|cKK>s6*C1Up at +QNY
zviNU<gQo$Id<<aPIraCCdu(iMdfUiDiN%3|WV#!^@b*^XJ3`+_*IR^_FzFZUSAt7;
zXLeXCp!?G&(1mk(pEdJ7-y3qhR;0FW=?nIhx;Q$TM)r6t%&3)&#}p~D7H+I0H-Ff}
z&v>$a=<1p7n{gIG+l2F*3r97}UaP048D+;L3&#yrJ9lax+(TH<r?;Xe{MSzz(}yq9
zwEy`Mq34>c0>^*9<VDzJ_usF~P at O+BI$uKb->)tl_`60>I$_dln#utGe&*C!xhz=|
GzyAU5D?ecX

diff --git a/docs/source/conf.py b/docs/source/conf.py
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -17,12 +17,13 @@
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
 #sys.path.append(os.path.abspath('.'))
+sys.path.append(os.path.abspath(os.path.join(__file__, '..', '..', '..', 'src')))
 
 # -- General configuration -----------------------------------------------------
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = []
+extensions = ['sphinx.ext.autodoc']
 
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
diff --git a/docs/source/depgraph.rst b/docs/source/depgraph.rst
new file mode 100644
--- /dev/null
+++ b/docs/source/depgraph.rst
@@ -0,0 +1,125 @@
+===============================
+Dependency Graph Builder Module
+===============================
+
+Introduction
+------------
+
+This module provides the means to analyse the dependencies between various
+distributions and furthermore to create a graph representing the relationships
+from a list of :class:`distutils2._backport.pkgutil.Distribution` and
+:class:`distutils2._backport.pkgutil.EggInfoDistribution` instances. The graph
+is represented by the :class:`distutils2.depgraph.DependencyGraph` class that
+keeps internally an adjacency list. Several functions are provided that
+generate a graph in different manners. First, all of them are documented and
+then several use case examples are provided along with
+`graphviz <http://www.graphviz.org/>`_ illustrations
+of the generated graphs.
+
+API
+---
+
+.. automodule:: distutils2.depgraph
+   :members:
+
+Example Usage
+-------------
+
+Depict all Dependenciess in the System
+++++++++++++++++++++++++++++++++++++++
+
+First, we shall generate a graph of all the distributions on the system
+and then create an image out of it using the tools provided by
+`graphviz <http://www.graphviz.org/>`_. For obtaining the list of
+installed distributions, we will use the functions provided by the module
+:mod:`distutils2._backport.pkgutil`::
+
+  from distutils2._backport.pkgutil import get_distributions
+  from distutils2.depgraph import generate_graph
+
+  dists = list(pkgutil.get_distributions())
+  graph = generate_graph(dists)
+
+Now, it would be of interest to print out the missing requirements. This
+can be done as follows::
+
+  for dist, reqs in graph.missing.iteritems():
+      if len(reqs) > 0:
+          reqs_s = " ,".join(reqs)
+          print("Missing dependencies for %s: %s" % (dist.name, reqs_s))
+
+Example output on my configuration is:
+
+.. code-block:: none
+
+  Missing dependencies for TurboCheetah: Cheetah
+  Missing dependencies for TurboGears: ConfigObj, DecoratorTools, RuleDispatch
+  Missing dependencies for jockey: PyKDE4.kdecore, PyKDE4.kdeui, PyQt4.QtCore, PyQt4.QtGui
+  Missing dependencies for TurboKid: kid
+  Missing dependencies for TurboJson: DecoratorTools, RuleDispatch
+
+Now, we proceed with generating a graphical representation of the graph. First
+we write it to a file, and then we generate a PNG image using the ``dot``
+command line tool::
+
+  from distutils2.depgraph import graph_to_dot
+  f = open('output.dot', 'w')
+  # we only show the interesting distributions, skipping the disconnected ones
+  graph_to_dot(graph, f, skip_disconnected=True)
+
+Now, we can create the actual picture using:
+
+.. code-block:: bash
+
+  $ dot -Tpng output.dot > output.png
+
+An example output image is:
+
+.. figure:: images/depgraph_output.png
+   :alt: An example dot output
+
+If you want to include ``.egg`` and ``.egg-info`` distributions as well, then
+the code requires only one change, namely the line::
+
+  dists = list(pkgutil.get_distributions())
+
+has to be replaced with::
+
+  dists = list(pkgutil.get_distributions(use_egg_info=True))
+
+Then, on most platforms, a richer graph is obtained because at the moment most
+distributions are provided in the ``.egg``/``.egg-info`` rather than the
+``.dist-info`` format. An example of a more involved graph for illustrative
+reasons can be seen `here <_static/depgraph_big.png>`_.
+
+
+List all Dependent Distributions
+++++++++++++++++++++++++++++++++
+
+We will list all distributions that are dependent on some given distibution.
+This time, ``.egg``/``.egg-info`` distributions will be considered as well::
+
+  from distutils2._backport.pkgutil import get_distributions
+  from distutils2.depgraph import dependent_dists
+  import sys
+
+  dists = list(get_distributions(use_egg_info=True))
+  dist = None
+  for d in dists:
+      if d.name == 'bacon':
+          dist = d
+          break
+  if dist is None:
+     print('No such distribution in the system')
+     sys.exit(1)
+  deps = dependent_dists(dists, dist)
+  deps_s = ", ".join([x.name for x in deps])
+  print("The following distributions depend on %s: %s" % (dist.name, deps_s))
+
+And this is example output with the dependency relationships as in the
+`previous section <_static/depgraph_big.png>`_:
+
+.. code-block:: none
+
+  The following distributions depend on bacon: towel-stuff, choxie, grammar
+
diff --git a/docs/source/images/depgraph_output.png b/docs/source/images/depgraph_output.png
new file mode 100644
index 0000000000000000000000000000000000000000..960bb1b5639e7ff615ddbd6cbf8a542be31d5fe7
GIT binary patch
literal 24719
zc%00<Wn5KT_xHU4ML<9rX#u4bK|lmS1f--vq`O<BLr`e}2|>D3x*H at FQBqn;>Fx%f
zvCg^v&-J;VcyqtH&%wiiz4ux(#u&fvZ_c?4Qk0j(!=c1Mp-_0zQsT-e6j~DeVaLLR
z-&~f}{RZDK4WCMiqy8fQrZ(n8p-?wa(&CR*T@%+P-LzHrFCJ};;GW)HV<`K|kcx%F
za>LfzBD>aDIw9D7-Soc4&y0!mXp=+d^#wI&_EX(*9+!0PYHg+VZ<k(Nd7?yyb~*Zo
z0fm2 at +uw_=p5vf<);KRkrh2~^(SAPmT-bH%jcapD>3xv$fRUa7-6(|1^pbKZhJFaw
zu$TRS0sP35=y=^Igd#L=6zy)OK(c6>$df0R+1S~Yo<G0hCyIu;!Jw+9hO=+XO~Jvz
zfkKgyk%e~|msD2=ynTy7O-+3xLP|U+Md%^_^4(!Si%QM8#l=4ZSx;FwIK-8d2yt+5
z?AOP6Nk~a&hYPiGb8~$(3vEAMtgWxlE-hg_fBt-LW0H!NmbO(vPfzdCrAt`S at 3_Qn
zKJ*AJEkZsZrJ(R7PbDWjI@)h-&Dz+^ETgCh|8tV>%*F=3f`WpptLx{sHVk=r`Bs)E
zhb1K?K1oSQeMOCpjd$NDv5<(q`IMjkRyj}n$&)7;k{TKmGBPrXs;a)}=?o-fWa*ii
zm&7j-6BE0-xutpky}-i76_=92VqjpH+1%{SxQXc#g>RHZL^%6B?jFD662ZXWU_?xe
z|Jj+RRF?A5 at i9FM3l<t08uGEf7w7%ec6y_uqk&|_GFhUpUNwc|_NF{~f0g{sOsn73
z+`>Y?#zrB3T#7e=fmq^_l16i1BVZKMJG$H3qytLj(@RPSP7gOF9UQpvX&(kA+_!6m
zenmt?wBieLaNt%|RT&11TUu69#>K_)zdGZGw!AnydXXsTg1 at k^a9!AwU*bJ&Xn%$z
z<HLuElA at EBm}xr2KO`i)OYy#-cyzu;97gvDUr<o6y`uwL at K2N1X)ujpJv%4omo%|}
z at WjNBxjAF)T8GOp5wYARIN7psZwMJBMA+toDzCB23xrT;JbOmUXEW~0Yc)L69z?t}
zluw at Qxg*{F<qIPd6DAWAlVO0lDmkYv3DG7 at S6}{fPT3S;8t?N}z5ZgoN2P5>>me|h
z5)RPjv+2L*bgzzPufQli{Cl=5B_%b(cv?TVu)x5 at ML6wsNVBuELqJUYmR!%1P~p)z
z&-d@&+wtF5Vn92bO5R?%_WRHKM-nnJIGERHLms?b&|mqPm#%xXxmKZhJ2EP&)P3L5
zdb)|m*4EbbbiI1J^(yD*fdS>Utx=4bHug?|ud{6dT-Kv^LuiF4q1{r_(k(&6%v%Fb
zIbXC|yjErFh~+Zcf;;@)neUFhYk)rW>eS}w=t#dKm~{Q1meJG`vc<~md9m&jPtS+H
zfB)9EvwL4r!9&e&k18!KH4GTO<>v1GJHNO|R9Balh=`~}zXjdb*H?eK$;&XHa{~h=
zW;{qOzXMW)kbuCa+^pBIH(6-3-0T`e2ZOWuwaet><QzJ6B#S*sV$HA4?>eukKivI(
zuf}OLW;CsoZ^lX>r1GuRNRdHj2*uI);q>-<?=uX$hHcF6KYn!JuclRb|9u4!dj$^<
zPrdn-kVGiuEoZ3$QrFD3Q49fxIjlEt-zq^4WJtdMo#Zk#>uYq$s57Kxc`%2Nl+=&Q
zs8hc`{fS{fyXI?{uxt8X!f37wI&;u^tZ^kibR&Y{Clk!H?U<UP4GIc6+VuYW?!E8{
zR&m1)4i9DMs6naemQFTBy at FPi^<z;{bX|{iUeA-|98W58!p5WD at 3ywKJ`XB9!t)b-
z?X)_)ecCU*%}dj{A*QKGX=r4$y-~kuINR!H7|?dXeET-ud!d8tx?cOw&Q|sQ5(!e9
zqL8s$I at w>v!p3fiQV<S$mai5>`EJ8p_q+F>9)h+>=Z#-MImzxg$lX0X8%AeKJfOrC
zgl5Yn`2YTGxHjFaw}*|9WDqb-VOTSglkzHfzWd{!-yQFCR*HU2q9K=`sh)n-1|eX7
zu%>Zzdist=KIhl03#Pa)gpE;qAfbAbXDwW}9=R?a10y4s=aGGZda3A|BFcWiq6adN
zkc#Tv(~oyrCMLAj>_`7 at eX!`%Ts}MAMM~M)Sb69`ie_P__r)%qZ$N<9osVKnQS^L#
zWP&amJnAmcd3zU^fZEyzCx at HwE3HOWhE(!Y)ys^rMa9JEqq=THbrDcf2Ag!pq@|^y
zp$O>ct_6v|LBqfl$sfY$9kQ&veperLlL$N6>p;VxR-A#0U&Z=QXJ}w(C{li)e9tT{
z2Dt4mq<#AINk~oMo&PnuFk!C~{lRQGiRJf|L|mPkCeQ{R{m-b3y)5NC^Y5`-mp;#k
zNgle0ioR)amzI_m)yYzp?(grHtD+z$mt>0$hMM}dzdr)i2NUDBSgchYhEjxxUS9Z-
z;Lf#VUa1)`;A9zfe(CaMrDxBcT`6u66h7M$16|;j(JOF5R6_=un4wW_N|99z^FfR%
zD=keM%6~pss8yZY)RY`XD<l)etU$K1>^wI$HRU+-8BLij+VT9v$sIxq8x>0ZKsmSG
zb=weK1Ro#2!lau_nS~@%k;Qy>;fJxEodU{yc_2$%yuzRj6J11DSU6KT4 at ytO`}fz-
zQsEYS!TVF0<xn-~vu!4-luS%+<*BlL`}#E%Dj)$W{*m`9q|Fdo(x}|*>`s>R(wG-8
z`O%oT*G1?&w_Yoq;9z6Zqbgqf=`>wkc0Q5}qqeAM$jz0K(iCWEX}RP>L`=LtK2~8t
zjF#Hhr)V&CwEYJgRj6GX*>tkPjxM5-BX7fvne_Fm6uO9og at wD5(~INN)BC4&<K1!h
z%&<^8HTEsYO}E9-J2kro2Z at nf*Y!T%$KQO)6qN>gh~tOuP}|VuUT4J$Nq?7 at msdUe
zl)SpE><vhtlM`=2VIkE}VNsEPXeeGmL4l%{)>RbCojVeWiUjbZ?*aF{dmzT6){lmV
zhe4 at Go1C?(6E~>m&{0r&Q<d|^%VajXVM1o-=lvf2JtGziz+d0GQ15nSKr<?wTUiMP
zJuPj!yENrHNJ~va(_e0;5E~blR#ipT)6?^VNrXnnu-}}g556ob;Vaa1beZY4V+GVO
zG}NzOzd{D+tSBJ&G`qUHuU@`<Sz<X~ulW^f<CkJZ<KFrNIjY3t(6+Hmr{0-&g&eie
zoAN#}k(xuZ0&BDB^iEH*(7VHF at 8*tIZ{EB?p`d6IOWgOT?Nn4$MBTf0PhKD?XuEfb
zYnayy#9(f2PMj_JQ`_mu-k<_DYHVz*Kc3erx1b<oWp#DA_t9S-{)o at v9^H<_*oldW
zEUc_Szz=zKb at 4ETT`etX{o-#huHc7 at Go(Svg-c|l)zt~??CssmQ<Y(5W&LqbZu)}~
zT_ia<xx#i*pw4}tjhBZfTsbfE9XaQ~{b^6JHh(;;FO@?T5bOP;rADCx<W at AGYL&_W
zTyY5rgGD;^0u7weGEXujLM`_{Scq-hR$+?<b{aBH0C7>((^HtApP$><kVlzAPg{4L
zqVOIH3FW1yr;j0(a){~F;?BZBrYc+4 at NiXsg&mVHa`_g$zbDV#_xdG62R_PVr9<^l
zvbVphp{W^JS;^bc**OqKBj_t&BP}5A;@l2xB^$3OD@&i5(bt5+Gc+<nOxXXjS4a|N
z4)rfIIe*BKm4V?6s&BXV(X=6MS63Ga9i76)#)h$}sf3!E8VL!B_{+r}UjFjJMkLCA
zO-{lP*0r4)7Jexoxo-6q;3nN}x=Fp>`8Jb6GKVvN0k(;W$<jzMUDMI;tFlP~&!BGV
zAca9f&vW}S*=<cktXg|}72x+mJd}8}-e)g<4h_-s@<um*FN~VaY*VLNcDC&3=r{u5
z);BX_c9vSZQ`~q!3G;TGni}<+bugqdA}9zuGBPrC$dc>TnX9U*Y9I|!TT<_p#=jS*
z>XnwH at EZTDqcX^Q{WWK2=cD}*Jta-e;H4#V5_0l)G|JW4{pMT0-*Fm_m6?n}HiKYw
z;0N&{GI`kb<;#`kzZZhCa&ib;WM*dv!v|EAmES;Bpl4;pu6Nm7D9+uOgI*n=w6(Rv
z<Ku&Y1Vbo!aW<R(3jegM95ryF@;F#~3<L`NgS at VkArVAFWQE8QZFphf?N=AaW}fH!
zBkHAwm;Ldm{EfbZ1<?$P6#~WrMRY=0;R06pN2M|Ghy*!?aS&8te!1|yr7Mb={$n?y
zRZ!3|MvC>w1q%X#WwKfk4ZJu%{WH_ at R!T;uwa#f(-^hs3*}Nbc8UHXS=>Z=;ker?E
z_Dadg`878`nsWVvmu$Y}qsm6wbi6<X#6I)(n($}#;)XZ7i at m%133mb?<+Q0^$;;1g
z`}8b-YdI(7Hz+|YuNB#p=dRp8lB)R;l)w+v&G2|<9=L|gv^P1_Wy<XeWDdee#L#1b
zSZEz2_WN5;V{UMAUW4#=-R>aY{;8tS(%LGzwk41S{WsWO8G?4B+b{l{r?P86b8&Hr
zU^KcGjId_<kmU=IiUQq6N<rr}9~i)7w*`uSiB;k#`^Ux#2>;RXVoF48Y#;<IJ#aPx
ztlQApT^%}F1{gi!#}*c>eAc5rnVC0 at jg3Edbzv(AAKy%N{f*t;-d?dnB~ULAD)AQ7
z34yqy?Jsl at adFt!u3eLj<GzfIjg58nDzTvWFd7UdQ(6A=V#C$p!Xp at x@R%4(5N at dX
z2!_6YxP#T&+6v;{j=%dFEEM=0MqXZ0NG*QPqr2a_yJtbRnUh`7x3{+gLqg`L9EFic
z8V*u at _6%Ea9)b57d$jEAY()MMlR+=$ufH|Zf`x_U6CE8rI?E at WKg96p5uI62;$wGr
zfq{X6 at Q4UBFn4BVW)FA1GG6DkBmo0~M at L6j+GZlrj#!d4n0%?HPusTteDR}k0shZa
zW^HI}M0oxZCMJ@>OOy5Yq5RB(7xVBq#eZMe)I<wTi-nD?71Sc)<HrC92}VIdDk!wM
z#l-=6d90ZD)Pc}zdJ9ZU%pLP=sy~ps<S<haU{&DFf50oXGBPvg^iLMeJ3K%x4leP^
zl>murPoF;hI`06wg6-tg{9H{<wE8I&ZeNLv$N!i+H at 63fxl>kV*fTRQcnwf<<L+Go
z#GYYciGV#LyncN&Pty4N5t4!7xkW{t&_pG5^}zpl!G;EHPK%!-BYzIoCqSuCeM>G{
zT3QBVNf0-nf_srM85xZ3`zx0 at IXVC1I)ywANY<Uoi~JzkaEXF0CGgq&E_ODh3A20K
zcHQW^Iozr9`aMdhz+9an{_v&{t_-ME|3|)58t_J(;k7sep<sw<^v92k^z>*#?t4g<
z|F*GodrHQk?qksAPr-3I|51;Cl=Lzc6&0A>8*FSi6IC`=Dh)8h>J6 at 3{`fTGpK9R0
z!PkV3QjaBqxQ5r!Qsol(2vIAa>ixn38yhD)FOC=e_%?0p^X7lBvasB^diCmf&5I1(
zF|!n}?2+V+Qp1j5wJ%mn$3HZ{zJN+8D=8^OIk_TDmMC-C)Sb-%`;>I<KG>a54%8PL
zAOAZqC)u*%;r;vMn+?18D8#|AaB?Cs0pf8975m6r7;35}shz(54Fo<xsj5C*s3-_s
zh9ran!=sZo#QL7=pb!1MW5_^ukx-%9#HV?=TCY>SE at STahi+xMV{a~B#X><(<ch7w
zD=aSgOnLu3KMiZVMa(37qgGpfXlMwt+YQ=?fs%zc9qG7u%}uS|WQ)dm8pH9}+?>Uj
zn*zQ7<4di_xOsSZu=aO$cj+Ufz&~CqRAc9O^l~AjZvlwUcV4rQ?BL)aT<Yf6-d;PB
zk<L&m at Dx`)JUrCZ)j#+4UIVTbd-iO|t68m5GZY*$$?WWGNoncpxHt;HDKG`^p&}U2
zR8$DZ*M~W}j5Y2%j!4F9O(g=~B2)(S?rs9Wg7TrbXSx)<977}QSu=J(CvI)c0l-uG
za#6mGbUbn6y3sWno);H%E;N&D(K5kdVYX%R+S=5B$7ynq2IwL=@<~K7w>8Rq!E#97
zB+{&N%quzjnWu8er=g+2*F>{0CR;vf+}Qi^<Ht~`vJNRVjSkkvZZ}QEeA8Mfue7QB
z89+anqri+pn7*-$R>++I#m36o&tCuPTmUUKS;(WxQiyloJ^tlVA3eIrJ+mGj^&Uo=
z&g&?|v9xZA|6D>trFdUF2%~<0e&xy)>#?%S0K at PFjNuQCM_E~U3o86o8>rSO)T`6=
z`vzkmcZA at f!ovv=Kd4 at mPfSI{&T$Ifv&3!J)YZeo2tpbuEizf65)zmaA=h6Q6mXoK
zPXBEx9;pL&f%?px;`t^rlE|Eg@<VjA0g%}Bd!`{J`0-kFHZL}J<1LFTy$~5j^;O&H
zHF8g4_4W5F6 at LfuKq+cyj4h1xX})^(3gtu0EPuVIZ^7})b&sV~(h)~+=GdrPOiU}$
z%gI!flx+Vr3JcZo<qI*PlAIi#hPrwm7)gt%1~+313+c+rN^x<7y-?;q;(1$l85tR|
zQCV48%lpq&RmD+Xr>C3yt<2Z&{P^-E0M!S)xeQ2?W7m8h<KW<cTl8^Z+`8zOqK3-;
z1d$+Ng!Ua~RH%fzfT^kJrPTgh70E1RLKOG?`*u!xHld|OMPaD$(9qCX=kZ_njMmZy
z<1>w*2`F6M8)_^hmwXT(Ve-64n;tdy`?sjLIPeY=%5iJvv5dSt6RHBTmVv?H#S41W
zhlB+7hk8s=aBVIwdSYVYNSUmg(K6Bu5m+zc<KlST at m#(Xot|z4$jiv!)YjLpM!%;K
zjFEZzG{JhX&z+cY8LS(+2xX{5vFzHQ6=kSDDqNL~m)gTBKsJPHn9S>B at A2kTBLZ&V
z!40OHyh^I7I=@CRayiUNeKqD at o2b6_;K2iy+qeC4a+p0mJ$)Ak|H^Y&@KC~Jf>&)^
zZg)}8HGelcs*bE<Zq2r-*EzBx;4nHiW&l@?g&=&}X#XoNw=V+n*+-CAa3f1_-uX}*
zF9|4&@6#Y5cnLHVSSifz2T)%x)oG}>^zsk(uje{%P8B|{vKqOA5@~*QPI<few5|t<
zMq*-&moHyJa^GNL!aP0PBrjTAT3G?ZjE3q-63lVI^VNK at o{?1t0v{L@)G}e$97^}<
z2o16i9YxJ=pZaVS12yOH0F_!(Q`5f#tjtI8++%BR?-J@)gPTe*$DKR)sFGLb?huM)
z^~Fh*_jP1-b#?#KJg{h}4cV?n7ZNU~6;;&dZqDWqI`1<=aFMxrc`er-o%z(hT;%(d
zkugzgGhTsBen*?&&H%>eSS}-ERTdIU!pX_eCXDV8?dnLeq=5nbPfK1Hf)~pJw-EMf
z3%J%?JVHT36B<f&KRq`Wi!v0Gl$3O|$xG0ulNbwf21P(bgpP9k6MA0}rtRd!o%*3G
z0St3PagC-GPz=ha`y=OS^p9jAs)dCG1idfLH@!2`)6wBNM}YjL7K2Q&2?>8Vy^o&;
zbU4p{BSnd*tH;jb*a(**xi#ZG;%NFKerT?eAWK;q)I?5x8)7-@<m5zRePM2n=hMrJ
zGq*GjcJ_RwvH9+g`@h}yhn{N*RUS<>Yq>Yd2`zk&WBh4l>qS6#>((s_jub8<i2&U}
zjcQvOv{ZOM6^GphAAgG1fkytR-FjW~#Lj#-Hp*tIVNCx4plocoBO5#WGAJh#R80f2
zpOb>*&<-$K+2FCtDk^TWv$Nyi<7a~sE#0D~0enOkX#~$(_PwnwqiZxWDvDKDI2n9Y
z*Z1!skZCL|EP)F>NfYL#T3XJ(yf2QK8 at F1o&i(l#1`%RxV<QK8um@!?3WQ{(vO+j8
zaJWE23|I(pXmH_dzpLcRVq#(eOi7<Uu|;+n^BA=HGOb!q)<r|LdkmqAT!c{~1X&)T
zESN}^Hf#08Mt+Cm*Q#GT=Z$x-vyKa-6Xb9?iPk7-<X7#>Qn0+gK*zh at 9dxRxs9*qD
zeuo*;^V*MY5%81?;OeYPMHnBZU#r at dRFI@)xKJw%%$WDZ8Ph7V6H$8SE3&%Lbj;Lr
zabi^}Mp&R-OJvu$Pv(BOF=<Sr!q^D}P(b5#w*AF0pzmv`(HG5vHbf1KB-*6m`mz20
z%YFtP^Z(XM-YNJ-li#)M8vRsq<XgJBq}30=R5EaL6M;KH`Ak&XwSD`B!~VR;Hz6TG
zJ*_N&04`P+=Q8a at 4l`=|OlL}{<e)QW<oc?q=I(kbM6B!I^TSXAyj5F381?%<K@;X6
z=CqjE*{{BS{aPY8(24xvfI$IO{DYTFfSR~5Af=G}FIo*~-T<ymB1ZdKrKU~E_xAQn
zhz2@{7u3%TT7@{N5E2sdzxeGtWsC;1(a`YF)y*x4gu)|sTPmQ`mJJ||iHRvRMmkFw
zmx9}u7=QIHbaFKtDQD)D;}YE<+bNnxG>BcAR4vwRBp at cvxIgZ8vYhh-T6G0^bEoO|
zSbFV2E-tReApP!pOY{z^YX+TQ5Ny|gx`-qM#C?Gx0;vf-Mv-MPs`ARpj+z&LP$&?R
zSRkjbgrxyVd825KWPnJm8J|AkKvX_^_AGQoRZWdAUf0sV04+|I>CDE^5FLmcXs)%h
z6BFT|?)kg- at 82I^)tAjuPRq{5thSr>|93$Pb-qrHVq;^QSzNq=+S=Z3nQHVfu(2tu
z{_*W!i!jlUltjb9!Exhbcj#{VsMw#hvqXHtS#UIfRFdH7z$@7AF38O)fklUKOoL`Y
z^Fa02`dncS;B&JxGblkp!M&BCt571B`ZEHy>dVXTK`i1y7K0i%nLjMB>a)r3=;#30
zB&RhoF>!Am`S~-osHjKZaM_%Xj}O_90{^hKwl)JchMAd}|G|S6V1c8F{(eHR5555b
zSG2XY^)0`BX_!>{C?_D3MMTc2EAH&fcSonr_w3A5yV0HW`1lyvO!M+;R2+eL94$Ru
z8za7R=Z?LzvwvY>VK{C<VIjDC_%Z;NMA_ThoBzxS4VBCA{xCj1E^)pTb{A+uQAZ~<
zI2h;W&!1R~x!=CYsH&>Y09lHnbKJh&0i{_{ON+h22rhsa95=U?i3zHysi~#u=0`AU
zSKyaYS)OVY`6?_VH4gJQ4liH&*&M(t$U5SS7hFX;^~2^9+`POt6~xURk{i%nD=Vu;
z!obi_Jh*{@kr9T7_vz)0MBRH_TrJaoFI+fu8?KX)k+n+wwXHLe$x at cG-QVAD4g3Ti
z68H at QgoT}*IC6FpduPK&n=QJvqvNuzt*!mbmv8Iq1+{f_X8-)b(9_e~TOPd92)^vI
zi%(G2ATtfPab%;=*!af1d&HnspP|-;r=$R4Uj>alIy%A<G)FcLJv^>@*VokWxV_Ko
z$%NqY^YeqUD)RX8>u`~)I5_Z;_uvdsV4q3R(a{MA2z)@P8F_dR*0y(YB0VN8 at fZTT
z2!{3Rw{M~_V=yF$i-pQCv+yGUwXx<oIXT&1?_yU|SJyf at sbeR(21Z#+YXU#C5^e}p
zmS$X1M~B9Csv!t&)p#jiOHEM`m8Fc03hy!&Mc>)qM;2VrF)+C7XG9t?M2DRm>oQ*+
z at 0es}Wz8OK|AEZEB%dVU7avav&UkNgnhwgzKNEJa{w_831`JJ(7VaP^&gHW5@`$9Q
z(5vKkZh#xt(9l4pkcScvaSODxVOv}FzJ7iNM_aS4fuT+fptgVDvq-m0O_{jw-FpG|
z%pI~+*VJsCp4J<Ey+Yg6)U<VQuweDdg`PpX-dXPY<&{@Z=@^)quRt20P>{E{*YBb*
zFmXi`6$wN|MG2^>f8D~rTQArj)syw~qy5}hLTX{p>v?&3ENpCVWSPYN-FPJ+BlCyW
z?5&O{Y>HX0^;`-BdjMREK|w)*bQiMX5>z^n&Gybt2B0OdGM<j3^0vQfUhnNWQa^e{
z{pCHKq`SMow{PF{EiKuoaq+1631D2X at bM*5guPrRY?wjAMU{KMeQO1-u?OL2A;AIm
z((g-s92giVA})RjSwU&?Iu&Jz2&Cbwf7c%+Z)0=&U~~GtPQ9~1Z?aI0^ExFpHT7LU
z%Jz;9dI14SdV2av!@U6|+v%pzM;AvJ&@X<6IV at z;wNULHSd9?CqbMdhQCC;~zxS$?
zA!G1q1boFF&X(r=&sl%~{8_+Fs80bwLD&#;ca6Uih>MHg0Of&koKbp*6^P~L=GOXJ
z#NEBl-oL%42Om%- at ZCExD9kX#NAbB|Ln0lKp#YzUxDqLOd4J792}egBCfQgYK-wt+
z0d**!VYEWQ7+3I-y}cU8Wg>`zy9TY7WaD|TfgSxJ6Gjc3DS at r{?PpNnpzOhXY1cVo
zJJeDmHv|IhOB3UA-O`^$7h3fM+UrSq^d3h4>|nwU*(VPU4xX8t^No$Y{?F3VJyYDX
z^Yae;%8BKYOqJ))1CZAJ=?ooh_I?D^+8IXkPCAOoU~RP2^OL0b$2&T$VKjmzK*?tp
zr<*0^<!vu}g#sXyjYdlh3<H>h- at HLb_ICxH*F<1~krh|ZBl}sb2i)9|lG8fc^73K!
zHzFb<-%#DRMV5eI)LZAeqChEIt~5*ZZAdS|Pe&{H#U)OwYCngEe{X-GlN1$2havhb
zIhfS9zdC{}2s*6}Bm4eahlgL_+xEayPNBWS8Dvd=xLEH!va6n>kRtJ(R>-%1+UUK|
z at Y37nwOLVdWN*I8ZaQpzqM8x(y{M=tmj49<vj3Qy%OZUKODUHIjK(;?FUSTV3I`7_
ztGMYT7@~DN!_D-jS#NSy^_2U&#Kc4?rl|hJ2QOI!1+~^n#-l~kQlUxxphMDZ(UA!W
zRE^u6R3GBwWh^T-*S2D%?&vkiLgMBawD}XFpmb!t7d{EZ!NF<AUr*Z=k(SKp?}Mt8
zXE)shZp-5AXdBE~%Rz8x5EMUugtt~kis=A6;-R at oLLO>sCA1Je_&?(A1;8XB&T~-V
zkzDh|2|F`o_99e09`6fJD0z%|sxojZmi4K|gbK~V(KI2F4Z-D`%*=zSkKanMMGq8c
zl+)HR8agkg8bZABK-hre<MB9HyW=wVF(yVrC0mvrt^tO44+wb&E3q<IvoKrF`#c)d
zNs=hY7hJJLn?GKra$Y-r;Zv)Z1FURp>Dzz45ThvX+ftjDnmUVX7ZCSbR#t$0-`U=#
z2b$KbFel{Y;R&QsE0<4xC=FrFgyMmGI)TZS&gc)MAzMxFNqiuNEIz2HsO;?Qq{F=L
zeq9u<WL>_=#1xs4!MMLZ at e!h5faT_}22}dNfvl%p- at XMiFfuN~{~88lF2aEFxNK-6
z{c&7U6mVP$AEhr1kb3n>_<Y^Yo4BetBm}oqY{fw{+7S$qMP~@b%?PQb=;yj=@$vCt
zVegy_p%jzQ(#mtkh45LA-f-rB144}4ySA=w;BeZzS2DezE%BjSWO at cN?GS{GfIm)#
z0Y6PDVjVwy`UL752C-reKDRS4$O(-tI(W)$ktR~=W`*amgOX8RetyP-mkY>_)rg*V
z5($o9?smz><NTok2+6NqT|uH^VuJHuo9|ijEr0m<QN`Sx1xidmXl4j0 at v11Y!Vfk*
z?Ra<b-GH}}5+NS#!`QX;bqkou^!C0Xv{dJlJ at EimcJ>TN(5R>=Vu&F6sq7dkmB)|K
z`~m`^8XJYqPgaX1Hk<P;c4!7HD|ud>x#HmBW`g1wb+*^VLZU>~);{n8MwVEwE-4B3
z^zvfm;rRfe6*_*{Sl1d>&q_n+;Zc2jw%dEvW@^$|r8`tEPjvt+N7t`kiLZ|5$(0P3
z*y23{$Zl%_IXOFmS{Ze=)#XOBKaZ4U$^c@%p4AK at N%_$A?h^?KWfPMur4ws>=A<y2
z!IqZSs6L=aDxG{Q%Ubm*k4+))Lyl)hFG|IhD{!YU7k_uWL$pI#nJC%ycTiX at DZ0lJ
za<&Bum8hnsX27)QTw(Q8E0~#xm>5hmvqk*%d|t=mk<ZO4IU|In)h-Ej2Njw&88-QO
z^M>)fR%B4!{Gm--h0VBJAA+)F<3xLfPYVcNrZhqR4z>poca4w7Tie)J9ITBwo*vk|
zl~k-V!SQ>e>%M%;!O`(1F)?x4S at ra*e})`bC at wx82^pc2r3_4FiKX#R1qGRL_sk*z
zJRdxG&>wm0X<*<I<kH^vd%fY6mAuF=JvJM2TzrRUZGI_ymuq}w#LCIZ?BU^oq=#Td
z%J=WG-1qP2K-M^$KPvR|kl+0LIWHNExn_eam!GdM_<mA|TqV%#+d6d>{Q-EpJ3Cm&
zb}H|Qc0V-Bk53^*_%_FgIghcKnIsDExL8S>iqAR~GyzzI5l9RvQP2r3J7e=D?@!3S
zH4?EnSFbYGYRiL-om*Lvfzpr$I#uEB#WS)vY4W3TNSV$MLYmRr+Z!@GVl>TB#(h%}
z#5688R?*V()AenF{-j<_0In4qSz=~+YrbVrwY)LBv#9_`0T3Pv>(z}W$M>0;H{n);
zS{Fc~)otVEPyZPO_v87GtbBY`VyCfOMwoK&NwHJ&cQMXBGFeClCKM2plaH`$1055h
z1e{hrlvsB~-hz~tVu<L2lHfO=BcH^8nwy`$#W4tx?zHlg=@eKp9ZYuPk=B2fJ~xsb
z!H*YeNea2|rH+qlLYz77O{G;@jYNiqhUOk-x~~>iW1#^0l{7Ueq421{x8su&z68gI
zLPE<kEpJuz^rmw?!E0BT_0qy at ->lS>8!6Vyfp{lE1qTM+9aRH}F)=e!0c<%x-}Gii
z85<cH*&m0~i;Xvio(9$Cjo8V8e%>G^j>1o<Dp1b*+S%z3x;9O}K3%-v><UJf2{kt}
zBcfGhP46fA7^08QZb}wR*`-t^hBz1;v{cTfBT;oe%bzTiJmv&`{{90XO at 8yphAmnu
zgda|~TCuJi>g&WrLY2*gM09jC6mS+s#<$1{1B&topLKW7IFwUVA9ISQC?Hv8zd5oI
zkDRkG>I|`PN+^^QxIMHf8?;~xX296eQby2agGBIyyyWJj`{{Z$>%)iIA?laTPxcv6
z72ba at B)SjRCq~vVYdsEa->%;R0fJP at dtR&yA01!H{W>r(ps&TOkW3OYS at R-eY~G=^
zVRE7-B{sqRyb~!I=zRv{%H?HcSg0gHmm;z8X1yQlXa8N1wHdDn@)2|~9XbBL*JS&X
zq=~`7AVL^_-;P#8QxiwBZ+m<DlFx8~2IIp=03;|yP0e^+?<Y^5T=D at 3(jq-=^z`(^
zLWR-@#^`#VJDmVBu`)B~$gO5%?F7&-6A)g$bgA#r#rCz*wj~IQLO|4kuMvzLflu at e
z3?8HQ{Ihv^d4Jf3`)_;*kBw#dK5h5IUb}j2Dnj>Z_e6Q<X=v at eU##gB6(3NJqswDP
zU&07ez0XChp)26Ruu;#RKVOYzCBZ=#QBzZEEd#GYfI^mK8PX@~U5HTvu3I-?<cRIf
zs>lc<-o3kuX3k at x{r_65rLs{0b1&C9_3I!eGE#J7W5azOZj}W?tXB9w<{Mp1CNTwt
zv|ZzV%&38*fV*DQNV%E9hv?|Z`kJSA_p-F;y!HqKv5*b=JGu>lb#(#+gsN=M%55Nx
zD=geRs$Mj6x%hL^)W7(H3H=D+e*XM93OO4SP%%yMkKyw;lmHWtcx12BgO>4eN=C`B
zb{Hr6sIC&bY2mrKxzvgZlH}y%t?g~XWQD&8T-p8Rsqi&qnKooe_2Z_Mxp__TsG*Tj
z1Q at -xfNMse>8=ZLX8LAk0dRFgfP-rXtuGd>vgycx)LIX|Z8+GGjaOQcLmPSO`pm8C
z2CeGG?h2e9ZrTkh5fDbi#ZjR3)!$dvny~ENxnh&iXh_!PGVLV^ibUfs*9Un(#;N<u
zV14 at 9Bui^wQh`&>NMvN>-nT02v2+*r&_MH(z2!?jP3H$%D`4S-E{^_)j at 7*Q1Ey4p
zAMay$c;8YjSWg>)%g3n`3bwX)&~C)ZGI9K~DA_gdfS!8TvJ2imz%Vm|s&EJI@&0n7
z7L+Y&8XAlB at k$2YmtPX>nphKEgaJ_VRM`aJ+wtjXwOh{W!h!_Ke74n3RZUHzva+%Q
zW>TX3kLF+uSCqU*2Z#R`T8|K2*I7UO^*?%lPspJ#Iep^PUmYuFMoR?_*4R8%`~NyG
z<9F@;lu(3E;L?O$^DR_s_4cca<3-W2U{ZE06rxqo*VB!9iC&#@Q#>>mrkDb)>aZf+
zMj23oOjWjJklno>_ow?x4BC88Y{p)tr>C0(1*)p6OIcRNf9vk{bKMYrGWzS}27`fU
z+r8e0Vn_{9Wn+EtAg-aYkuEs%p54?#s7>Jzf-;!Xh29stDI_E$H^(`02L_Z^hVnm7
zx&6T=W+Ff#s}`!wwRm<g2$;P}j}CvmOyXOvc>cZ988`s^Z$?2u0seZ<r%%CPmCVP=
zOlZaxw7$plcF%Z7I4+9ZVPpHI{{R&n8ai~U*WJ}+p}Kf>yek at K%k8&euvA13fNvRf
zK3U&bVRC9w)CfFQVfjBx$s<30t$vec78Fc?{5;0&lDm6k3;DUTwUs)e>+xfB8w-M5
z{Lh#7%yF_3RaI4cADhpfTrU$46ukdw;+KDpTK+QK-=iyTuCCz_fcwjX0c3ZyrS|vt
z|I-6i4Grmwi;KPA?>H&5np0k#1bzShUEs$^#&b2bfo{%bWFw$;xxapz`2TN3n#aw4
zad!4GD%WLmN-hR&#6zLt;J^(9 at E`byi;DmnxP19?KL8ok4{NLLgWbgx?}4kF4aiwL
zq;3xn4<nEZvEr5yVfJ+O?NU!5$Bi34&=TaL751~DmgeS51JXGS4T(@CEygP>3<Cx%
zc#mz*p*|Wzt+9{KQFqM26F6xLD6px}ye)J*7X_1P{FB1b`|s&ALQME{!qyeS6jt8~
zwW`g*%k$XJh$s~Yxj6rzWhhWDee?atk0;U(6&`M1ItR*uNXaWJi{!KY#o`&h0RzF~
z{_y+VXqhJ}Ir6u(w6wnU^zd0uQ-=DR_9n|gk*{!=m&rKzF*+%q$p0AnMM6LzGE$^N
zESM-?*zscd&E>1$b@)ykE4X-^ruaEHWPd9BD*syyserf`e0=;pNP{4vw`%$S7&BQ}
zS##(m=A)}^>XMp`?z|u#0{|UM00xEugUgjG#!KXSo{yFDK0xYS&cLJcKHvZ62-m)?
z+Sj(>o>?e^B-GThU@#pI)-+1n(&pxj9h{w+9zA*lRqEsDY>Dj+X667Ug=AT{2n5CG
z+ at AfgdVcX|o9U(`_k%TQ=tcSz?uVeKg=&L{1}`iukkHV`!BiRs+yrBhZI<E{<0F_7
z-t4-aju at M8@SvBkl7;*EUA~No=^Wq1q4Ojm+v`J2%mA^%L!_+>-{aaA at 6^YA_&`F*
zZH$F<s?4NYU_C-dMg|8MAreR*ag@#V$(8RWs_h1M=DWLwhN2Nm(Gg7gv#;-lv${@?
z*AHHx8%8&G_ekwKImqe0whfl=0s#dD9J5x$V57t!?7#%2`}<=eg9jxkLX|CxRV7=|
z-Mwzqp!7~|W~MK~@@3W4%v4lV?z=zkBj-^K1BOf{4=)`8b|FXG#wI4BK;+JMQ~qXB
z-M6hUvz9fcprGKf8RvnTkoEX2W`DAfM>~Gr4<1Uq^P at lSNeBraqgdG3f`^NAy06F-
zXh4AAQ}c at qX3H@?efkuXH0}MPi&!=`Hn6JQ9y1EhCL7$M$T-wR0QPRcJL2z|$p?5d
zBso!sN{BU|?Y!H{GJ1RES~l1|=W!Jan+T9o<YJJ1k=^q=R>K at Ja&lfQx>QzlA3621
zuL&Rd00c?{Zw3-Fn(wa+xgS0 at J}jOT{-&j(B9^aKnC|0)0{2HkO8NwT8aTDo9)6%x
zWB(c%jQ^ZejAD{Y7Yo1-qEQO~;L3cKuhtJm77JBdUq6Uc1dqdw`Q at X@zOiyMi~jT{
zBxGa~;KC91e0xp!uB!+A!OTr at m&wfr8UjM(Sp}!nVS|Y(8*_8>&zaIucfC#=A3}LA
zDJ!$=-5A9{*2=1ECxh{*_-+UZ(M(NENl8h4{vLO4c6F8A*;NABCOq5e=0x at bfgf&s
z?B0ffHX6v3M$RNboU%Fx79_Do%TNoslhiw}w;rrdL?k3!wX?H(xZO at 1`_PRW(40Pc
z#cTDd74v$s<A$1!&h53I;M{?jUiW868vSk$LJsn~!*D6-lrtp?IN<<Fw)Z3nzWCja
zjqK}w`@o72KWNO22&r~_C7!ITtkF`VE67R?4AtX2RffBF2{I%?MXjwl02*dzW^Opg
z7KBn#QijLG_(Qm7W at gUxKKd)}<t2oPN9ija#WWkloGfB)o;zybOhpq9%~DhI7xFmJ
zU+hU5t+t~h!MWVp+Dbr99sr(5{K=D+&d$#3X^!j=%TjW39kXo#5s8T*;F6C{P7IMA
z at V{Vy_UW6NGC9X4g(>H?ml}NukBoc+?W2c*wXCEBtJIFH%>8HG at b29bIlZ+n0=nSa
z!{{D`?d>^c=HxKmzKsV6iJXdpzR<eO5w^FtZ?UM|lz1xOqLQbIEL$FIOuhpqLKcC;
zrEXpazK1?@IW8&s`uVkVcXyAbIaz>AfBp8YHHet`pS9cv4ws<iC8ed0RaHrlcZ1Ep
zgoznUWBE}<Mdj`Ptcg0WUFYWJwg*@Hb>0E4M$f>24)HIcuYcp at aPu1Y!cwtUON4|w
zySpGOe&uGpI6(_g&+hW_c6@$~K}bOn6u~ITaOV!bt*xzL!1o)<1GOXT6V)%E4Vl^5
z47|Lg;KAN#7W$W$-$TZimNu-U<Sw!n4du?$7(2D9ij167w>?iK#~vo%*w}bxZLD08
zEgD&91b`$YCVmSOf=m1GI+#$yfHTE@%gP$pZH9rtLBFI)$O$Dqy=%~=qvK<Pna{7W
zaBxHw6$#MM(fe`~QslRzf9r&57D^Zx&^tIdfFuVYhuT0H`Mob50vpjsNX<aphX at DY
zQoMosvc3OD{*%hhBo7Gi#0M`i|Fg^uBJ&zPC|0P}*4m2x?;$pVFt*c!^{WaBy#X7Z
z^=kP~q@}TuEr+C}P-Mfy#Drc at kP3?T4TjP2aYN+r-_@(cL=`D1deu#QjAWIXp+Nh{
zrU*Fquiw9mLm)vDQ`Pd(P#_{(`}=;y#hk#P$fnBC(NQWWv;B(~%T<h-fJ+}fd_WFi
z*4Eb6cph`ZZC}iPBLx9YMV>;KnVo%|ntB8G--UMM2qHQLh5>lb9d`Y~A=aocdM76*
zWB~4H*W6h1K^}|h{>XWiDzmWgc0)tM%>4Xi$Yxh}_tv2yQsmTGem;9*Qqt_&+Ar23
z1AtnD<sl<$+;*9e&63tuQMff0vOkln%7*euN}@r=zuDV+)(i=xz2&xhHUbZ#W&EJW
zJnl=*xo&Q5xy8lgD|IzBso)5=wziOa{F<Dc{rwx=%*>2mK%lj&OZsRmPErm68#z0s
zp+SK_=Cs|fn%7n%MQJrP*TG{;LN3Y3$oQ^nGemrDYZK$-Iz2gQ?d`n=)|jE0`yjPW
zcECbLOblaZXUCUjA2|h?lEQhWqpUozltPK3rly{OJ0geY#>ODyF&%2D;S0E~q_Wa4
zC<vQ_gM;$iNJ~?bkFspK$%~3IRN|<<tc*z97fce2#0*rw+)tlGot$_rEiF?OS$r?4
zJbymI!UV|T>gtNsHPGGt^k^(cfv%>m&bg1$*l+;C_tm)okacimBoXo$-oKl6ot0K2
z$b)&H-suGevYWLrF)^a(Wo2b=U{(qW3k?hm(2x^n8X7GF14REGM+=w9f;1Vl`m<r9
znbD85C3NNI&=8bI^qPB&&+=4%Bh4!;^i$3gH!-=jva+K1{5kW at SdR%5%F4>Brl#ie
zj~{qo7ww&#`~m{5Kt=YOYV^oJretU5ji28Wj{?nMX{Y1NK}uwYSb_u?`JY*zZo2)@
zG3fC}7GOV1D=Q@(olwyJug2WScBZ5;3S@&SG~U|UTHn$V2Xt0lORKG?2OkDDynDW+
zs>&Zah3s92)F6sN-Jz+e8C+_qP7Y=eFO1sp(dx<y3?{NgdUE2f`s^8UN)8}8H#Zk$
zzeo}z&R|jTW2#;bqfb!I2E12dQyE+{E9=(U+L~|X0;mCX>ZBm$OGihNSBu82KDLpL
zwCw?OuL1$Mu_50K16oJD>g{LIAu*6M$sx2tnrrsz_dCfkG{d8!25p3sdTDj1z5fb<
zb=m&P_&$&ZXLW&pxnky9H2c=!A-N!K=SG5NVW&SH)m@`b++ at dA@h at 8XS6wsP>gqsL
zkOV0={Xry1(#gNf0~k$BN=o{0z(8jlYA14b5INT?W)Ko3|1$X9J2VtHT(Sp5og2Nt
z9b2o#&A&OlkKc`^-Np2Yk}rp_NAr0U-dR at wueN^shNF@#O9)p+o^MivX+ at p)-o88h
z%=7R0q3cHN;*qd%!Qf2hU`TK<4n)g#z_|Ly56Q`4FBf~Z^LU5z;8POtsrL@*ANS7>
zrx{sTup%NNU_ at f!X7>P<_?JuW<P{a+YgJj#S_~8n&SnncM$2?WVZn7ieDDQU`yR_B
zwx)R9$~Q{>DhZC?DE-}GQ$6o<-fY>pR=uw80yOdhw`We%%8Hm688McZmtE(hpSR=t
zaS0_lUibKloFbv8r at zi+=nGxU4nQ7jizqAOKHF-$7Axpd1iv&z&T!B&K%V;n+-A!s
zu{pDZP_#?uPyiH%HigBP_Q)2h{b~Mt&T0BR7OhtMjx+ziuglBJV>Txwm-d*i4i|py
z?rzI3ZbClH8w8z$SnHAwaH3#WEBK5YHU&0^StyXsHoVdox4+bn0itbUVq)*$KqN at r
z8Sl6@%0(^cjO%@IoCA#-=Ad2X5&LNFsQ2Qtx>=>;vNA4>013!FDNIIu2i&|);pHwX
z-*B8xlP3kx^gj+C?p+xO_jY7Vg1Q~SXi#8*9FT;f_%<imT?QFOLcKu?BzVEhhJXCX
zjC9ozDnWrpIcAF20Y#>=u)lE<on6*#P at T^m at 5npw-*UZ;xuX+IE99<!ur>yBr=Gth
zm2(+{36z8cf^!dMY6foK0zhv72?E1YiSyo3li>RGKv1XBHuFpfN^ETGrT&a7naZp;
ziLg;K;B{gJoY)c*6M?$+Xbm+>^wA)|nJjVrjm^xAwtshA7jQJsa2g{6Y3*!q+a(|)
z!>rZDzk2m5sPAm_bKS=V25Bve$gt3PZna=^G+B>vLoNU9N3M(Vk>3`k(lVPYLd|^C
zdxc;LQXY(q-sa}ASWi?H4)u~-)vwsM$2&nnqKnMW&&v&?iF9g51I9J<ElzqpI$bMw
z at 7^qA^nddPy{x1p71AM?OiX3mNQk$(hlZMZ2`JXR_rHgf`@K4#(2H`%g*2V*JZ<E!
zd)L#T<8?4*CTB|wAz=QD%Fz0GXP;M9-zl<;fm#A*V^<bd`gaU2hJ{+6tp8Y{Sy=LG
zYAP9}2o~FdhqC{7{RqP3V4+pDwAeLq<*;#aGp{{53+KLfZ_`5vkCG=ahEq=-peH>(
zo)QQn<NWk6s=4_QT53%Vf8(O+^XF0E&l^ux@()Q5wd at +7064)fnK_70z4)qn>U4B!
z9sIx>W20_sRs=%s=Ya`Fc2ZDZfBi}XrPs2S3|aqny{?z+1O=+R4CTVM?APeuwYrN9
zdHzC;JN^)QQL;=KT~W+b9CZBlH*V`T#Ny!Kgiyyd9q1~U^(4}x08dOxJX?LSZ+`4X
z9tM(I2TzS`&TuvzrT6b_d~nnpFVf6NP4yw;)RhI0wf^!o5b;eY#3s9&Af5=dcX7Ho
z?Q%*}sY7uULf9`&EZ{f96K3~@aKO}0UXsb_p`oFpiRlYWW at hHt`1qCR`$J7dYg6uZ
zRs=t8+jaf-9QADCHiVPnvuC${D&-Fy?F}jr($eCCEXT&hwSYuzZ*NP<%iC6+|M$Q&
zW_NFrU;uJ}4J=GxSXk$$XZf`8f+B$D|2!s#8}p^JGrg{k60C)_O8r&xJH81CR2K);
z%@iEZSx91qJorE?koW_yO$4kQx`OO)gBkJxC4>vi_!Nw*{Lfi{xjA3tN!-5|=kDuy
zLzZWYkN!S(b>*LPcwo#;0eDT-QK+0(V%$aK>+dhh6!n-Xik^puq&w!e82poQ;5d;D
zV$te%6?rrRpc*+F0>#Z{NWk2p-crWR?SWdMrr*+%xvPiApCp&5mfDw#<3A;yf at wmb
z?wNk?V;{UN=y|l|RXwdV5#1k{v|slIF}7!CXSgwGq$7%s-U`UR%Fe>LU0(5m;oYB2
zYWZ2f)O^PcK!Tq>ePUop2UeawtV~}X%ppbz*w6U1?9d20_m%ewKLsl!>S1bX%Jbs4
zxQwi<_4d!M^Mdy=<CdJr)8A;Rl5}1_^^akCu~0t}ACwQ-Bnx}RL)AEkiE=lQvoS+`
zZEJh`?c2A#m8nzUk_wv%zO-bStaKPNo_gVEnOjdJCGTr6-?<Z}$`-9V4Rr@|mHF1K
z${Jb-vkf&Zt*1djLCD=9i-=Dmx{UYM%6pNg;NrDE9+vAsX?}}Rl$H+Ctq0~~M*RdI
zLiWd2MiSB&iasfFjvQU2qoV_}TWefn`J2#x_hnWVso1POz0!&))@_VOg;!VKhY$*7
zV`rDnQdW|Xzzn_4^#+9;;tmS*_1)@XPJ!ECL!!ewGLi&ckTsxq&q;BYqBxZK_FkN5
zTI&Al$e_`CAqgfXCeiPFe0(3m!$rWN->TG{oU>ZBmPXbZk*8e2qa#aTB|QM0*eJZ~
zcT<Pf!)S%#{jbp#x!^=E{r$4Jy86_@!UB1U at JkpCBQOEF$nL at q;*~I}`(aSm#gI)J
zwftoOL}a-OU4)R3P_%Kr`{UKkww0m$WvJK4XC>d$lFHj4N$w*^UR`ZHCW_<t2JEcO
zRD(uMPYvi)BY$IK<1+X)WFZ%>wg>Y at jG}ztVEk at 1Eo)t-P;C&fkCmSO^_)X(Urt^g
ztI)xyQN^oscV!k5xvIZ6CZg|@P9~W<sj`uxxfHGbdLK4k6atFM&cnkK4!B>aQ$MWl
zW);=5F<C!xb`~Zh07!AkXJBApZ-tzcln}LZc*x8^<gAJ8N+28G$g=vkt}d=K!_BnQ
zfRWI6&5VkQ56I(&^b(VQ9*5y;Wi&?0gC(l1t0O|WA5OX?6g-D2xz?v;)SE1XY}+GG
zhpHF7^9C$;@5K;-GD{xw|JY?R7Xos}*%uWR#S$sM*Z#k59&P;{uRpDc-4Vi{Nr|&?
zkGKBRojx8|?}jxtD&r89GfJY?l|5SUrG(sNbFy)jl+%{A;MT{$Csd+=JBs4sawbA+
zHpFE0IP+<d$=PvTn-{Y^+8L7h%d&q?Up4luRtheToHdyx#%&&-_Z%i-+!Yk87kH7K
zm6ccVY|Z7z at x%D>Vgcv1+%h?Qe0&ZDhBx4|a=Q*+X|2cztc(|HQr${2MUH;#?(Y}6
z=)8=QH!+!~`rB9+8J3ntkMa2uIvO$7rI6mmd%`Kbt`C#T8WmZLL%3q%<G+D+tWr<b
z*4N)ZzO}G>jO!!AtD^#>(9UQ3>nk-4&4l(q*VnK5;HGg=3A|Qnjf_Noz;u{C+s9{2
z)6egGEN}8U<+iii#GGLH?d>H47DV}0+}cF7!u)h?DWgjVOLeKz>!v1Q48_f$)twy&
z%-rNXwLSW%F3Mm2**ukcjmeI1n=z>FTDO*SjojsQPD#mGmU#xNY$jq$OSz(?ZaNL~
z-d}E5c8`<IuFBk9dGKN3><G+*^Tw|SRqp$i3xEE!g at lGupcdxmhi06)4*oI at Jb&u3
zs-mmgV`yj?TVG%QEuuOif-nJaMbK?0vs0lM=9Mk#cx|kl>fAtoZNAojfi}W+wB$A5
zXDJBc{n6}NX-O{LO8Z$+=iLRl`R35yEDe>B@<tPVAF>bF<77MKlOJ{gYHFIf|CpRi
z>S%AzwQ;ur-;SFr_}e(7bZ*WVlgAE6>2cD3y7TAD`#6ys`cS!?hFQZXsRULmSKehF
zDL1@&JaW9dI27ZEiBJ6%RRTPP`N#76WW5W;){e2Y5y>NakemG?1t}^0rlzKEoTOV0
zzVoxQEr^a#)3BM#42;#j%;DDPNw{C!MTgm`8?%Nk*V{-=LQ>+qK3+OAGb1s<`rFRZ
zU5 at K~_vG3E)cF-pOlN0jT+~FlnZnz1w(OA=c2r<!sENq=pjD`im<mLj?lTRIF1STp
zK!9YDC_2<eGR~XkU6B?}8_%`VGIuedFzMPo2{dH!Ug}G8Uwn~p!oVv3>-6ZT1dKgX
zgp|6B49 at C!W&U_%4^-ysTV*x$0s`^#i;MSQ at Bkp|!5F>^4mJb?58>+6*49>+mBnRe
zVbOP}O^p8Vp=7i9Z&MGUwHL`V3A5hhLYGDcKHB$R{BbE-85kMI26GgsycC>{Go;P-
zSBAbd>(tmAl>aTNeI}u+tNXpPGkZ$uAfJS552OItg)2|>8)!vbWhF1hg`QMj)>Cp!
zpUYRSl>h#1sP^nx2N4m`hsCS9=eSL$8-kcV6jW5 at Aa*6lt12pO?S?O41n%9t=M1Q+
zrliyYu4fdY{sTGp$aAT_Q9homyz@`=3Ms-SP{*vlhSRgDgxp>pdM73(D!S6hzt at u_
zDA)A9?vTAmalj2jtJ>B?LPBB<CY`MqKGfKsk&^Aa2GsQP^bnZb@~J=wSCxLt8&jGV
z-l|u_1sb8ROaWnVQCjuRHjS$`Ha25GFZ}iVdvcNzq30evRSzMkdpua#+731*C-gl7
zuF=_6DCaftLsC7@`TP87_S?5_S3~<pMyh)bK*{)k4J01X1&O!9uv1$9Sy}m{TslT1
zodex?05$mw;6+vp;bWuf7L96Kouq at pM!gq7kv&u_?CeGi5rH;iWl!e!VOB~3Jh<7}
z*`sIcMPD{a2|BOogPk=NO$!I4E}NOruS_0Pl9HA-h+>j61TQAbLc)!Kfiaw;K*wh{
z_0Ys5L7l at CfJJ|@&dEqXKtT2;QR!=pD`g;>BOb}~&tU4l_w|J%yH?|2?AlDovlUE4
zL6i at xx6Y1Vni?3iWGkdxNK7cJsda)kj|&RICJqw+-rfClzKR{<=(b){qK3Nq-899o
z1%-tg_V#!4^YZdsP60-=%1!ZBM~dkNM at Gz`OCTJdWS>gkBm%I_wl>n&{|v(SrM0bX
z`h at 89iTTN^j?r!mOfxexxP7eDO~am^p0%ynw*4xbALqeV9~`F?_4RL*S643zgz%Yc
z{Hld!D;n8#w_NTJDtq(h4W;!x?+ef2G_fl^*;KqV>(8Bd&6<Bx-b%XvAt~txs^sTS
z)gm#Adn7Ix7u#RxYVn^KogMvoFRP$X+(JAHYH#$A_I*cZXO)Ao%@yRsm3kKFv_>JX
z8wl^;zrVDy!obQJw5m8BV%L052J_;l&aogs5<0WA6iCP at F@ya50ZC5R=!B8K=m>ep
zTwGG}C4tVw<J0I*od at i4kEIu{0Wf#|=b50#Z!urc;P*3BR#skr;Bb>x*z=!-5Qtgr
z0CA~z>6yD%9taAGDJWcnTg?lQq(1 at 4ECJ*Q>@dW=e%EK!9?Rmzp84BH=Ii5?{4N{R
zkpG=dEaFeK8(awy$ORO>MV(%0p1Dir>FH^|zw&HuZcao`FJ;wUipu%#g%>|R|L3VG
zT0j+>;qq`Si+3!`4SrXy(ITsVM?2k|rEs&NHhLRX4GjtiVHAq|j<yeI#TP=tbYY&x
z12RxGLL#C!z*3ByobQp#g8#be?d^@Mpn>Fnun!#0i0IM!R9NVjk-><Ii;J8jjwhqH
zGoEU2L;L~Y at 9fOXXD5E&T$)hFsYsC<BH%%}xw#1_D7u{ZGx5e6-0oFbkG%m5m*JmF
zdMurD8Cj=>{<Zz?c!!+1Mp}rh(v#iM4uo)!jpqqMhK84?aai=a(a~I#0{Hg;_UA>P
z+uMCt!vp<gz)b*hB2Vj9T8%i+;L{fdN6KGiyK at J57}DCtCL%UgYSBfC225EC@(?4y
zIU_fB<N at JMPug-NnX##<KiI}ZVK2>$O)v2ps77f29+o!(Q2b#3RWL|Gz_P at wm-f=7
zOZuk=>qlq1y|wrO<~~Rxz&L at q41D+Qt;mfTft#ZIX>CrZ at vptT*Dhba9Q*IXEi=o@
zT~1QIVj3D6$iZN0YHH-X2GV~CMjBq^*k*cq`VwV`%P3SgF56%HHFb5rfg~O4Z|?MD
zWM)W&a2=hV{(%|b_d5B%TDa~&s at pGqjqH_5GO}JGD-mzVxOmNLMzRuRMcJz)T&~TP
z8!da3n^hDcn~acKq(n#=$qdQ(o#*$ye^%e_z0dhR=QEz?oTKn#VWE~St||}p-=iBN
zrK^0v*MbL`;BPtl%!DF%mV;G)e5j<6k&!_&vIZ#>^nBXpW-uzq1~x=<mqhunyM(ml
zd$W)RGBRwa2D+|J#oWRIo_#a|a5k~9kaYf;%SBkQKN}(SoavzZJ}|K#>Vjm&j50BR
zDWSaqyg*q?tNrRqHEL2)EC9osf5!KclX#WP&npO(=N3>5KtutbqEn#YkdTYRW%iH_
zySK_;Z7~6tip$7Yh1knvpeixca)^cwz&lSC{!9CrS(m@$<m&GaF9Rf%1Lp9cMEQb#
zjyA_1lALj?NqXJe+t#?Za}9F~Y(J_vKbk^_k7wH4+!U0SW+kxkM`mOk?j&;N)jkSM
z5<ypJpjza>{QMZLy=~xr>VdX!L}{rstdqXhj!i8)rJ~}*=I`Ilt*tcC(b4F(E=Mz}
zhK<CL^p8G4>wDkBR*3#vK;X9a_FRi%V^{?bvZC7B+8Qz$k{3WIZc?&$X)P=)P(3~%
z^VA6}_9LBvG9N1FQ9X0U?ELvSV5_E)k-PJ$udhH(e3_h#JS^+*s9uJHA*Z;Qum_HJ
zW~@O)_wD|{=cYnbouTVL;!s29m!=4AMNe*|Ed%tv9s<L^w7N>(sJHd<k_K!1tv{Px
zIZ&h^M10l_<LvB=?xj#d<>@4#$wwt6$IDuUF}uI}!zXul{yZuz1zBJu)(VQI!sX39
ztK`Jg=W#{D3cZjQa+kXg`p*ndf!AukMMYD?rjNeh-x&ju at ImQ!?i?QmvNAgDd*b9t
zbB8ywoN<DjagI(-)UfhcZQ<&U;N?jj7JlWbk^D?F0cK5H2$z%Q&MNQwRnM+2kac%=
z*W1?@wFE$dTQ9B|>*(x*-Cq&B?8GMQm1!r_369Wven=V}6rjE(O--tm)z#%M%{1uL
zzP^6D^v_!gB?|nLvi}SQX!OQXGktd<{NkC<bsGZ4Mqub5JT%$;*SyndFxXs<lXqD#
zOB54zvq7N(#&mxLbe9jNAGKXH?krqG7+z*yH&E%UOA1uqUZS}pbv_b6R7grH@%DUJ
zcTW$xZi4q9bnF;Cx(;!0IO^fyfx(~|CTg2sXi$qtb2Ak%^@&rbEM>s6a>Qw$P*kML
z-t&FF(XcUQF{b<tGP_$~2bBETbLS*=_2Q?yZXZL-Gd5;J_mlwB8e+F2I;V24442JN
zirLYn)b;Dv(Iw5`;M1;!t#9|QEdh5bGZH$dGzB?e*X0K-j-jjIjue5En>$<8UpozA
zhw^4mu^!_}SRQfB&VBik$IQ&E+jX@*c$s==Xs9M=@#y9aHJ82zak%@6k9X8A=;-MD
z9WZwgAVP>M!DnYH;N8$r9)z- at x>{#(tO1SpL!%18fj_VdUz==a{`&p9h?3Ib#_bhm
zUq3&RAr8k1tT6$U*&IcW=KIZjdQsjzo~<5!v^9p+!k8?fMu9!v1o0Ug8{<~LsS1+9
zHtZ9F`@HyU6%|Mo#7z77`IFo!Md)G_lt$)>i;>;zQw93BZMM0#fKT;IOmxl7In&b8
z(B%#6GKflvl#7dt^ic6jbV1Zjl6>9W9ceA~PiUy}QPp4!Gc$8dT^$--=I!GXQCoZZ
zRz^M#KR-VUuObVuvIK<ef at _sNLx_C3|Ho83p0 at YF=>v3hbSD%P+DdI-oGl6m+(N?z
zwFqnt?-`=xV%FCDe~dQ@`1tq)ug&b=T3=ue3JOBs0m60Q{d=!C+~g?gqImEiv7q3u
zg2F;gvD at er4MMjGuMYK^)NhPkw<GAs4&@Y3I#h3Na|42RfEJvyFVD}9g7{5+sC0H4
zF8j>e5OndP5IDJ*g-<W?b8;{Mm1sgC(Eb2W+zWPx)<eL^b&IdGU at yW}&Vm1rMj~ix
zYNFQQTDBC9QVsoL)|K`3j<@-`Buk6(15DBIYGjSG)mSYoC}QI#Qd}YsZiJMON-n*b
z5%2;gTG`b-X9qqd4i1{5eq`I{p3Nl5b0^#e1beg91%Nti<hw=i%n-E9?d==Zwzf((
z9nAGcs^GhHAt$0X{0&V^I#;H9h-qomy1Kfw`}U!py3o+jIfjNe=A-t?z%}+S{hMEm
z-%Nlnd`r<_{7o~HOzYJPs+&~Uo<!`rRO9!Z4gB2FpT$P<lb)`w=9#zo=zd?X(7d3i
z2rI?qJooN#y7wH~b at 0V#c!_sVkglmIho`3}V%9P(k%Bk5L}OlaGqdH6 at 9uc4kP!~&
z1I8Cp)u}!HkoAkJ{Ayb>B2R3pnSi}L;;pX32eAX6qXLPEZ{K1dKN=_&M0`D?fwI@@
z^2XhGcp$%`f&;9F4!oQ!)etlcfn?h$&Jc|~(UJaQYH_qyzJyFhV-`RLDP3vP=DBnC
zZro|VZ>Z%E%`ZZu&q4(S01ylEMIGvcBZ^J3OQawNgIB&Xq6+z*4B33Y?|tZe6r{`W
zga>P_T{0>)nH?-dl_7cg`JdWO8REU8(~`MBlsBhKSa|>VAcrRqrpm*2B>CgW`YtYF
zVkTKEKxwcKKYaM$&Ce;s6PK2Jr=+B$1<!Z at r4V8WaFBL{f?x3o2 at _LOQKO@FAb?)>
zEd($KrTbT<oju+Tn~`+*^!nWUg!$C5BZzd?*JG at uI@%Ks&ptc$^%Oj23NfFYn(~=w
z*%z9465Vl#;qKw at D_s*-QQ__A=m3_C?SREz27$8Fe)xjmsZ)od=-I6d1>#q+Rr3l@
z^75#Ylan`Bv-c7~o`Cezb~b(UgiAtFvNcWAu;S`x<!VvBK~)gZu_-B?cjaAEQ&W+;
zbYtO}bt8svPYLMZa5`32cWlV-+zKc+a&vQI<Ki>{RQ>&D?#AIw2j6l+d}oGAc~SqD
zS^htdt7Q$8-y9?AIbOaT4O*i&Q!Wbh<pdwD+&vC{TVB|ob#6H)ZSt at 44i3ppO&Z+1
zy!rNZfdER_hkxqrL5u^1p&nOLas1u8NB&9TK|Ry&L)76_vGOW90$Bq!M&{-U+1cBJ
z^nBzgLNY)n?J6oN+&nxj0|Ok8&eqRda1ZOQt_G_v1*^WE_NAq#Zyg=w1A&F6j0XN3
zeSD|?YrxjlR;GeGC#WOJ at tys7%iM37!hiQ%u!gvk*Y at 3_a?Hxg>fW=G0{iu(rl!XD
z)9_(%`^kK#^E{7{3`QQA<@0#<b31d!;aP!^n-u|)oy{Lt8IQvfemz(B{-Qy?;Q)5z
zGIhi)K$L2OaJ<_|h- at 7BT=`^vikioJH9tQ;RFrb>9uMf`JaFMP;d^S}@JL=AiAT^3
zs7cuDsXf!;Qc`V2xcg1qT6%i1Pwlm}|KFtv(hrqM1#hiM06e4;i4n_FoirHKO;%N<
zkeQh&sHn&_Jv}WhE898NaQ*G8u<MZGPCHw_0w(LdC#Vrz#c4;jv|vGwOaAd>Ma`9<
zdQe%d)ocn!Qb)j$$hGn(PZFsdkC|k7l6IFGiCACXr`_}L$4eONa)*ZW^1_0G3ZJQt
zO87-`KmsIU3?SK4d$4s!FwcSQ=>9B#r=@8EAUWsyIqS at IZj6ho>$zin at Eb|bHx|cT
zG1ed<$AUlYiHnGctOH6`*+8g{AOCBB8vdD3SSZ@&B*(|k|9&hxFEyv6gbBE$dzf5O
z0^%sAygX%Y&I7TBMUjzv)fbJ*KG+11P_SwOk!aT7#md4WNYpbmGovEv6+AUw{y-0Y
z|E#6u<2H%gU2|uXs{`fmAX?B+I8nnpRp>Pxzvn=q=2r=%P3dfHC9s3}1zeMC;%;q;
zX7*YAh69-qB}e!8?8g!xZ~KyMpl-xC0KF3mY*+;<QGV$hGC2VvG69;f^y6YQ^Wn3*
z>Y$x51Esb~7}@MS8qCDR#O6{fdv19-I|dx7-NOy at QBrLsv&12G0~OHH4+FqtTZ9PU
z%&n~%?i$g77%S5f!utU-pnIUc2d60|VRZZUp&}g+VPJn4n3$+BiM&c|m;rG0n5Gg-
zGE$Zhp1;JzoIX~$2aNTMj|*T<TpLpbrG7wER5aC~5Q%+2E(`|U)M6Zej at F_E(94%a
zk4s8cqQS5Cl}rkg#Ps#`Bf!p{>5S#L#l%>JM%7X%6lc&F9*fL-Dw^t7M{EB1_f$vP
za{bpYONnTgAD6q<KvmtBx*O=@<6e<OCQG8jjsvdFp9XkTRaG+^8^g(KI<ZAy!$kk$
zWSsw4Eos|$v*F_(^KHir5~&gp!o|s{MO%3P>dJ}}C^F0Ew|k~Nf3HmUwzNbtMq^#P
zyxv4cMV-gta1H8P=8w}$&p(R+rI0c^KQB+zYXg)xkHtoI2gXE4OMK$y=B at xOSGlz0
zO~S8wJV^42dinWP*nM=unwZE^y`iILtMUGd%_Z2p5X=@}CZ+=i4ghlATDgaAKa)j-
zwB-TzgcIfdR#8E0@^|o(;F){(oaN-?kRM%HSxIj{gs-1}l&X=i^siGy6Vlw=#D^#9
zDX3-!%@_VP_dea5A!J_?pgaT;qz7sprKO|u<?mDZ4ul3ENsGn=0XF)8=jPF=xB-i{
zqt~>tf~0f2bP2zYnX9R{mqj3aTW(mWK^`=A%tR}pb!dpIE@&~vRQxO0Wd)`)mIj8>
z4)xrSF{rK#<USUQ{k!qxg- at blV#0!g7_hO(UT0+;9TwW3=QHylnd`nvkc^YFb7jb?
zOHFO<1V|^KGSXMy)2OEt`OKBCU8pG+OfHXxzHSdLOZ5n9U2}Jr`0?Wh2R}c~WZP2{
z%xKiJXYy#mb7m&D=g8COqIV4Lw4x7#H2Sm7fFMS02v&?Lv_e8c><I6REYF at rbGA@*
zAczXom<`4j+?Do}*<gq+_mLw=!oogB<x1$($nRUPa!Q6+61l9b1I_h2dL|T&yCD*Z
zHGpoYR3g9)Uj?RQOb)!Ewv`nZcp>j^y+?q!(LUc?crjkXhRe6KvJ!?*J`=ju^xp_&
zUZtyF5 at cWHBzQB1(D*;>Q!x^|{a;fy*osad at BIf4;(_oY`irObV^Benu!u<Im0_9J
zmrnNHJH?D at NyftW!jM?P<rQd|+S(?xwP|~f)gO_NkU- at _;H+<ig`HaYtuSY0V-t at W
zM!*vw!rl1!S<bbO4`MTTW`QBZFx@{k9$4S$&-wytp9a5n(bbh8VNujXp;(anZB;{7
zXdydM*82LMgvfy(hDB(IsAFw7MET8SJ5Yrrl5TCCom22SE2^s6K=n=h{F$7qMK~@n
zIK*Xn?i`w`2fqy&w&|V>fTa{zA9~kIYioEQ+~JFyc>&_HsV-ezU7G(IhSaRAWZ*9U
z#W8*mJm5Sb6=_0$%@q>W-+{A0@%5e^6h)PU^704HmDxoYiD`|$3Paya6wz&IZ`b*)
z=Y@?8H60YX_5^@^WMl-{k-$JjKzvYv)F8BAXScwvZn(bj->_LncXviZL&Iu^!hUWo
zP>Vl5*AXHksYh%5=utJ{%^hXPLO^_Q{q)G>oj%<>GIE5CjcsPIFyW(<F#es6KA|cW
z(quB@!p&ynEAHP<0F5^KGJqNUEyh7D5S>EIfaL7$?RE6<D6#ukeT6<OEDQhyg8?-1
z4+x;DSAEQ0&sVA%zRSlMM+;agE-j5JD8bJ!Z7w?>Ja|y!IEfnL at 9&SqkXqQ5cm|L#
zB<P)=TpA3W;<Fspp}^dzE;Tzl1&A^i1aNzMdp919nnGUI(h|)m!I(NYu+`eJVg7G=
z`{`3UwO}XY78e(%RTvo=A!m(Ddd1VHktEWgm;STV5gJX?E(cI at 5;(AW<XmM_EQ1hj
zV>=fHgAqiv0HrZ$Y(n@{E3JPYcuTb;Kdi}tWe8E)K3tKWdQ$O$+9*7W(bqB7F2g!T
F{tpk90)YSk

diff --git a/docs/source/index.rst b/docs/source/index.rst
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -12,8 +12,12 @@
    :maxdepth: 2
 
    metadata
+   pkgutil
+   depgraph
    new_commands
    test_framework
+   pypi
+   version
 
 Indices and tables
 ==================
diff --git a/docs/source/metadata.rst b/docs/source/metadata.rst
--- a/docs/source/metadata.rst
+++ b/docs/source/metadata.rst
@@ -5,19 +5,19 @@
 Distutils2 provides a :class:`DistributionMetadata` class that can read and
 write Metadata files. This class is compatible with all versions of Metadata:
 
-- 1.0 : PEP 241
-- 1.1 : PEP 314
-- 1.2 : PEP 345
+* 1.0 : :pep:`241`
+* 1.1 : :pep:`314`
+* 1.2 : :pep:`345`
 
-The PEP 345 implementation supports the micro-language for the environment
+The :pep:`345` implementation supports the micro-language for the environment
 markers, and displays warnings when versions that are supposed to be
-PEP 386 are violating the scheme.
+:pep:`386` are violating the scheme.
 
 
 Reading metadata
 ================
 
-The :class:`DistributionMetadata` class can be instanciated with the path of
+The :class:`DistributionMetadata` class can be instantiated with the path of
 the metadata file, and provides a dict-like interface to the values::
 
     >>> from distutils2.metadata import DistributionMetadata
@@ -32,14 +32,14 @@
     ["pywin32; sys.platform == 'win32'", "Sphinx"]
 
 The fields that supports environment markers can be automatically ignored if
-the object is instanciated using the ``platform_dependant`` option.
+the object is instantiated using the ``platform_dependent`` option.
 :class:`DistributionMetadata` will interpret in the case the markers and will
 automatically remove the fields that are not compliant with the running
 environment. Here's an example under Mac OS X. The win32 dependency
 we saw earlier is ignored::
 
     >>> from distutils2.metadata import DistributionMetadata
-    >>> metadata = DistributionMetadata('PKG-INFO', platform_dependant=True)
+    >>> metadata = DistributionMetadata('PKG-INFO', platform_dependent=True)
     >>> metadata['Requires-Dist']
     ['bar']
 
@@ -53,7 +53,7 @@
 
     >>> from distutils2.metadata import DistributionMetadata
     >>> context = {'sys.platform': 'win32'}
-    >>> metadata = DistributionMetadata('PKG-INFO', platform_dependant=True,
+    >>> metadata = DistributionMetadata('PKG-INFO', platform_dependent=True,
     ...                                 execution_context=context)
     ...
     >>> metadata['Requires-Dist'] = ["pywin32; sys.platform == 'win32'",
@@ -71,15 +71,15 @@
     >>> metadata.write('/to/my/PKG-INFO')
 
 The class will pick the best version for the metadata, depending on the values
-provided. If all the values provided exists in all versions, the class will
+provided. If all the values provided exist in all versions, the class will
 use :attr:`metadata.PKG_INFO_PREFERRED_VERSION`. It is set by default to 1.0.
 
 
 Conflict checking and best version
 ==================================
 
-Some fields in PEP 345 have to follow a version scheme in their versions
-predicate. When the scheme is violated, a warning is emited::
+Some fields in :pep:`345` have to follow a version scheme in their versions
+predicate. When the scheme is violated, a warning is emitted::
 
     >>> from distutils2.metadata import DistributionMetadata
     >>> metadata = DistributionMetadata()
@@ -89,7 +89,4 @@
 
 
 
-XXX talk about check()
-
-
-
+.. TODO talk about check()
diff --git a/docs/source/pkgutil.rst b/docs/source/pkgutil.rst
new file mode 100644
--- /dev/null
+++ b/docs/source/pkgutil.rst
@@ -0,0 +1,143 @@
+=======
+pkgutil
+=======
+
+Introduction
+============
+
+This module provides the necessary functions to provide support for
+the "Importer Protocol" as described in :pep:`302` and for working with
+the database of installed Python distributions which is specified in
+:pep:`376`. In addition to the functions required in :pep:`376`, back support
+for older ``.egg`` and ``.egg-info`` distributions is provided as well. These
+distributions are represented by the class
+:class:`distutils2._backport.pkgutil.EggInfoDistribution` and
+most functions provide an extra argument ``use_egg_info`` which indicates if
+they should consider these old styled distributions. In this document,
+first a complete documentation of the functions and classes
+is provided and then several use cases are presented.
+
+API Reference
+=============
+
+.. automodule:: distutils2._backport.pkgutil
+   :members:
+
+Example Usage
+=============
+
+Print All Information About a Distribution
+++++++++++++++++++++++++++++++++++++++++++
+
+Given a path to a ``.dist-info`` distribution, we shall print out all
+information that can be obtained using functions provided in this module::
+
+  from distutils2._backport import pkgutil
+  import sys
+
+  path = raw_input() # read the path from the keyboard
+  # first create the Distribution instance
+  try:
+      dist = pkgutil.Distribution(path)
+  except IOError:
+      print('No such distribution')
+      sys.exit(1)
+
+  print('Information about %s' % dist.name)
+  print('Files')
+  print('=====')
+  for (path, md5, size) in dist.get_installed_files():
+      print('* Path: %s' % path)
+      print('  Hash %s, Size: %s bytes' % (md5, size)) 
+  print('Metadata')
+  print('========')
+  for key, value in dist.metadata.items():
+      print('%20s: %s' % (key, value))
+  print('Extra')
+  print('=====')
+  if dist.requested:
+      print('* It was installed by user request')
+  else:
+      print('* It was installed as a dependency')
+
+If we save the script above as ``print_info.py`` and we are intested in the
+distribution located at
+``/home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9``
+then by typing in the console:
+
+.. code-block:: bash
+
+  $ echo /home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9.dist-info | python print_info.py
+
+we get the following output:
+
+.. code-block:: none
+
+  Information about choxie
+  Files
+  =====
+  * Path: ../home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9/truffles.py
+    Hash 5e052db6a478d06bad9ae033e6bc08af, Size: 111 bytes
+  * Path: ../home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9/choxie/chocolate.py
+    Hash ac56bf496d8d1d26f866235b95f31030, Size: 214 bytes
+  * Path: ../home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9/choxie/__init__.py
+    Hash 416aab08dfa846f473129e89a7625bbc, Size: 25 bytes
+  * Path: ../home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9.dist-info/INSTALLER
+    Hash d41d8cd98f00b204e9800998ecf8427e, Size: 0 bytes
+  * Path: ../home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9.dist-info/METADATA
+    Hash 696a209967fef3c8b8f5a7bb10386385, Size: 225 bytes
+  * Path: ../home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9.dist-info/REQUESTED
+    Hash d41d8cd98f00b204e9800998ecf8427e, Size: 0 bytes
+  * Path: ../home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9.dist-info/RECORD
+    Hash None, Size: None bytes
+  Metadata
+  ========
+      Metadata-Version: 1.2
+                  Name: choxie
+               Version: 2.0.0.9
+              Platform: []
+    Supported-Platform: UNKNOWN
+               Summary: Chocolate with a kick!
+           Description: UNKNOWN
+              Keywords: []
+             Home-page: UNKNOWN
+                Author: UNKNOWN
+          Author-email: UNKNOWN
+            Maintainer: UNKNOWN
+      Maintainer-email: UNKNOWN
+               License: UNKNOWN
+            Classifier: []
+          Download-URL: UNKNOWN
+        Obsoletes-Dist: ['truffles (<=0.8,>=0.5)', 'truffles (<=0.9,>=0.6)']
+           Project-URL: []
+         Provides-Dist: ['truffles (1.0)']
+         Requires-Dist: ['towel-stuff (0.1)']
+       Requires-Python: UNKNOWN
+     Requires-External: []
+  Extra
+  =====
+  * It was installed as a dependency
+
+Find Out Obsoleted Distributions
+++++++++++++++++++++++++++++++++
+
+Now, we take tackle a different problem, we are interested in finding out
+which distributions have been obsoleted. This can be easily done as follows::
+
+  from distutils2._backport import pkgutil
+
+  # iterate over all distributions in the system
+  for dist in pkgutil.get_distributions():
+      name = dist.name
+      version = dist.metadata['Version']
+      # find out which distributions obsolete this name/version combination
+      for obsoleted_by in pkgutil.obsoletes_distribution(name, version):
+          print('%s(%s) is obsoleted by %s' % (name, version, obsoleted_by.name))
+
+This is how the output might look like:
+
+.. code-block:: none
+
+  strawberry(0.6) is obsoleted by choxie
+  grammar(1.0a4) is obsoleted by towel-stuff
+
diff --git a/docs/source/pypi.rst b/docs/source/pypi.rst
new file mode 100644
--- /dev/null
+++ b/docs/source/pypi.rst
@@ -0,0 +1,195 @@
+=========================================
+Tools to query PyPI: the PyPI package
+=========================================
+
+Distutils2 comes with a module (eg. `distutils2.pypi`) which contains
+facilities to access the Python Package Index (named "pypi", and avalaible on
+the url `http://pypi.python.org`.
+
+There is two ways to retrieve data from pypi: using the *simple* API, and using
+*XML-RPC*. The first one is in fact a set of HTML pages avalaible at
+`http://pypi.python.org/simple/`, and the second one contains a set of XML-RPC
+methods. In order to reduce the overload caused by running distant methods on 
+the pypi server (by using the XML-RPC methods), the best way to retrieve 
+informations is by using the simple API, when it contains the information you 
+need.
+
+Distutils2 provides two python modules to ease the work with those two APIs:
+`distutils2.pypi.simple` and `distutils2.pypi.xmlrpc`. Both of them depends on 
+another python module: `distutils2.pypi.dist`.
+
+
+Requesting information via the "simple" API `distutils2.pypi.simple`
+====================================================================
+
+`distutils2.pypi.simple` can process the Python Package Index and return and 
+download urls of distributions, for specific versions or latests, but it also 
+can process external html pages, with the goal to find *pypi unhosted* versions 
+of python distributions.
+
+You should use `distutils2.pypi.simple` for: 
+
+    * Search distributions by name and versions.
+    * Process pypi external pages.
+    * Download distributions by name and versions.
+
+And should not be used to:
+
+    * Things that will end up in too long index processing (like "finding all
+      distributions with a specific version, no matters the name")
+
+API
+----
+
+Here is a complete overview of the APIs of the SimpleIndex class.
+
+.. autoclass:: distutils2.pypi.simple.SimpleIndex
+    :members:
+
+Usage Exemples
+---------------
+
+To help you understand how using the `SimpleIndex` class, here are some basic
+usages.
+
+Request PyPI to get a specific distribution
+++++++++++++++++++++++++++++++++++++++++++++
+
+Supposing you want to scan the PyPI index to get a list of distributions for 
+the "foobar" project. You can use the "find" method for that::
+
+    >>> from distutils2.pypi import SimpleIndex
+    >>> client = SimpleIndex()
+    >>> client.find("foobar")
+    [<PyPIDistribution "Foobar 1.1">, <PyPIDistribution "Foobar 1.2">]
+    
+Note that you also can request the client about specific versions, using version
+specifiers (described in `PEP 345 
+<http://www.python.org/dev/peps/pep-0345/#version-specifiers>`_)::
+
+    >>> client.find("foobar < 1.2")
+    [<PyPIDistribution "foobar 1.1">, ]
+
+`find` returns a list of distributions, but you also can get the last
+distribution (the more up to date) that fullfil your requirements, like this::
+    
+    >>> client.get("foobar < 1.2")
+    <PyPIDistribution "foobar 1.1">
+
+Download distributions
++++++++++++++++++++++++
+
+As it can get the urls of distributions provided by PyPI, the `SimpleIndex` 
+client also can download the distributions and put it for you in a temporary
+destination::
+
+    >>> client.download("foobar")
+    /tmp/temp_dir/foobar-1.2.tar.gz
+
+You also can specify the directory you want to download to::
+    
+    >>> client.download("foobar", "/path/to/my/dir")
+    /path/to/my/dir/foobar-1.2.tar.gz
+
+While downloading, the md5 of the archive will be checked, if not matches, it
+will try another time, then if fails again, raise `MD5HashDoesNotMatchError`.
+
+Internally, that's not the SimpleIndex which download the distributions, but the
+`PyPIDistribution` class. Please refer to this documentation for more details.
+
+Following PyPI external links
+++++++++++++++++++++++++++++++
+
+The default behavior for distutils2 is to *not* follow the links provided
+by HTML pages in the "simple index", to find distributions related
+downloads.
+
+It's possible to tell the PyPIClient to follow external links by setting the 
+`follow_externals` attribute, on instanciation or after::
+
+    >>> client = SimpleIndex(follow_externals=True)
+
+or ::
+
+    >>> client = SimpleIndex()
+    >>> client.follow_externals = True
+
+Working with external indexes, and mirrors
++++++++++++++++++++++++++++++++++++++++++++
+
+The default `SimpleIndex` behavior is to rely on the Python Package index stored
+on PyPI (http://pypi.python.org/simple).
+
+As you can need to work with a local index, or private indexes, you can specify
+it using the index_url parameter::
+
+    >>> client = SimpleIndex(index_url="file://filesystem/path/")
+
+or ::
+
+    >>> client = SimpleIndex(index_url="http://some.specific.url/")
+
+You also can specify mirrors to fallback on in case the first index_url you
+provided doesnt respond, or not correctly. The default behavior for
+`SimpleIndex` is to use the list provided by Python.org DNS records, as
+described in the :pep:`381` about mirroring infrastructure.
+
+If you don't want to rely on these, you could specify the list of mirrors you
+want to try by specifying the `mirrors` attribute. It's a simple iterable::
+
+    >>> mirrors = ["http://first.mirror","http://second.mirror"]
+    >>> client = SimpleIndex(mirrors=mirrors)
+
+
+Requesting informations via XML-RPC (`distutils2.pypi.XmlRpcIndex`)
+==========================================================================
+
+The other method to request the Python package index, is using the XML-RPC
+methods. Distutils2 provides a simple wrapper around `xmlrpclib
+<http://docs.python.org/library/xmlrpclib.html>`_, that can return you
+`PyPIDistribution` objects.
+
+::
+    >>> from distutils2.pypi import XmlRpcIndex()
+    >>> client = XmlRpcIndex()
+
+
+PyPI Distributions
+==================
+
+Both `SimpleIndex` and `XmlRpcIndex` classes works with the classes provided
+in the `pypi.dist` package.
+
+`PyPIDistribution`
+------------------
+
+`PyPIDistribution` is a simple class that defines the following attributes:
+
+:name:
+    The name of the package. `foobar` in our exemples here
+:version:
+    The version of the package
+:location:
+    If the files from the archive has been downloaded, here is the path where
+    you can find them.
+:url:
+    The url of the distribution
+
+.. autoclass:: distutils2.pypi.dist.PyPIDistribution
+    :members:
+
+`PyPIDistributions`
+-------------------
+
+The `dist` module also provides another class, to work with lists of 
+`PyPIDistribution` classes. It allow to filter results and is used as a 
+container of 
+
+.. autoclass:: distutils2.pypi.dist.PyPIDistributions
+    :members:
+
+At a higher level
+=================
+
+XXX : A description about a wraper around PyPI simple and XmlRpc Indexes
+(PyPIIndex ?) 
diff --git a/docs/source/version.rst b/docs/source/version.rst
new file mode 100644
--- /dev/null
+++ b/docs/source/version.rst
@@ -0,0 +1,64 @@
+======================
+Working with versions
+======================
+
+Distutils2 ships with a python package capable to work with version numbers.
+It's an implementation of version specifiers `as defined in PEP 345
+<http://www.python.org/dev/peps/pep-0345/#version-specifiers>`_ about
+Metadata.
+
+`distutils2.version.NormalizedVersion`
+======================================
+
+A Normalized version corresponds to a specific version of a distribution, as
+described in the PEP 345. So, you can work with the `NormalizedVersion` like
+this::
+
+    >>> NormalizedVersion("1.2b1")
+    NormalizedVersion('1.2b1')
+
+If you try to use irrational version specifiers, an `IrrationalVersionError`
+will be raised::
+
+    >>> NormalizedVersion("irrational_version_number")
+    ...
+    IrrationalVersionError: irrational_version_number
+
+You can compare NormalizedVersion objects, like this::
+
+    >>> NormalizedVersion("1.2b1") < NormalizedVersion("1.2")
+    True
+
+NormalizedVersion is used internally by `VersionPredicate` to do his stuff.
+
+`distutils2.version.suggest_normalized_version`
+-----------------------------------------------
+
+You also can let the normalized version be suggested to you, using the
+`suggest_normalized_version` function::
+
+    >>> suggest_normalized_version('2.1-rc1') 
+    2.1c1
+
+If `suggest_normalized_version` can't actually suggest you a version, it will
+return `None`::
+
+    >>> print suggest_normalized_version('not a version')
+    None
+
+`distutils2.version.VersionPredicate`
+=====================================
+
+`VersionPredicate` knows how to parse stuff like "ProjectName (>=version)", the
+class also provides a `match` method to test if a version number is the version
+predicate::
+
+    >>> version = VersionPredicate("ProjectName (<1.2,>1.0")
+    >>> version.match("1.2.1")
+    False
+    >>> version.match("1.1.1")
+    True
+
+`is_valid_predicate`
+--------------------
+
diff --git a/src/CONTRIBUTORS.txt b/src/CONTRIBUTORS.txt
--- a/src/CONTRIBUTORS.txt
+++ b/src/CONTRIBUTORS.txt
@@ -5,7 +5,7 @@
 Distutils2 is a project that was started and that is maintained by
 Tarek Ziadé, and many people are contributing to the project.
 
-If you did, please add your name below in alphabetical order !
+If you did, please add your name below in alphabetical order!
 
 Thanks to:
 
@@ -13,14 +13,18 @@
 - Pior Bastida
 - Titus Brown
 - Nicolas Cadou
+- Konrad Delong
 - Josip Djolonga
 - Yannick Gringas
+- Jeremy Kloth
+- Martin von Löwis
 - Carl Meyer
+- Alexis Métaireau
+- Zubin Mithra
 - Michael Mulich
-- George Peris
+- George Peristerakis
 - Sean Reifschneider
+- Luis Rojas
 - Erik Rose
 - Brian Rosner
 - Alexandre Vassalotti
-- Martin von Löwis
-
diff --git a/src/DEVNOTES.txt b/src/DEVNOTES.txt
--- a/src/DEVNOTES.txt
+++ b/src/DEVNOTES.txt
@@ -3,8 +3,8 @@
 
 - Distutils2 runs from 2.4 to 3.2 (3.x not implemented yet), so
   make sure you don't use a syntax that doesn't work under
-  a specific Python version.
+  one of these Python versions.
 
 - Always run tests.sh before you push a change. This implies
-  that you have all Python versions installed.
+  that you have all Python versions installed from 2.4 to 2.6.
 
diff --git a/src/Modules/_hashopenssl.c b/src/Modules/_hashopenssl.c
new file mode 100644
--- /dev/null
+++ b/src/Modules/_hashopenssl.c
@@ -0,0 +1,524 @@
+/* Module that wraps all OpenSSL hash algorithms */
+
+/*
+ * Copyright (C) 2005   Gregory P. Smith (greg at krypto.org)
+ * Licensed to PSF under a Contributor Agreement.
+ *
+ * Derived from a skeleton of shamodule.c containing work performed by:
+ *
+ * Andrew Kuchling (amk at amk.ca)
+ * Greg Stein (gstein at lyra.org)
+ *
+ */
+
+#define PY_SSIZE_T_CLEAN
+
+#include "Python.h"
+#include "structmember.h"
+
+#if (PY_VERSION_HEX < 0x02050000)
+#define Py_ssize_t      int
+#endif
+
+/* EVP is the preferred interface to hashing in OpenSSL */
+#include <openssl/evp.h>
+
+#define MUNCH_SIZE INT_MAX
+
+
+#ifndef HASH_OBJ_CONSTRUCTOR
+#define HASH_OBJ_CONSTRUCTOR 0
+#endif
+
+typedef struct {
+    PyObject_HEAD
+    PyObject            *name;  /* name of this hash algorithm */
+    EVP_MD_CTX          ctx;    /* OpenSSL message digest context */
+} EVPobject;
+
+
+static PyTypeObject EVPtype;
+
+
+#define DEFINE_CONSTS_FOR_NEW(Name)  \
+    static PyObject *CONST_ ## Name ## _name_obj; \
+    static EVP_MD_CTX CONST_new_ ## Name ## _ctx; \
+    static EVP_MD_CTX *CONST_new_ ## Name ## _ctx_p = NULL;
+
+DEFINE_CONSTS_FOR_NEW(md5)
+DEFINE_CONSTS_FOR_NEW(sha1)
+DEFINE_CONSTS_FOR_NEW(sha224)
+DEFINE_CONSTS_FOR_NEW(sha256)
+DEFINE_CONSTS_FOR_NEW(sha384)
+DEFINE_CONSTS_FOR_NEW(sha512)
+
+
+static EVPobject *
+newEVPobject(PyObject *name)
+{
+    EVPobject *retval = (EVPobject *)PyObject_New(EVPobject, &EVPtype);
+
+    /* save the name for .name to return */
+    if (retval != NULL) {
+        Py_INCREF(name);
+        retval->name = name;
+    }
+
+    return retval;
+}
+
+/* Internal methods for a hash object */
+
+static void
+EVP_dealloc(PyObject *ptr)
+{
+    EVP_MD_CTX_cleanup(&((EVPobject *)ptr)->ctx);
+    Py_XDECREF(((EVPobject *)ptr)->name);
+    PyObject_Del(ptr);
+}
+
+
+/* External methods for a hash object */
+
+PyDoc_STRVAR(EVP_copy__doc__, "Return a copy of the hash object.");
+
+static PyObject *
+EVP_copy(EVPobject *self, PyObject *unused)
+{
+    EVPobject *newobj;
+
+    if ( (newobj = newEVPobject(self->name))==NULL)
+        return NULL;
+
+    EVP_MD_CTX_copy(&newobj->ctx, &self->ctx);
+    return (PyObject *)newobj;
+}
+
+PyDoc_STRVAR(EVP_digest__doc__,
+"Return the digest value as a string of binary data.");
+
+static PyObject *
+EVP_digest(EVPobject *self, PyObject *unused)
+{
+    unsigned char digest[EVP_MAX_MD_SIZE];
+    EVP_MD_CTX temp_ctx;
+    PyObject *retval;
+    unsigned int digest_size;
+
+    EVP_MD_CTX_copy(&temp_ctx, &self->ctx);
+    digest_size = EVP_MD_CTX_size(&temp_ctx);
+    EVP_DigestFinal(&temp_ctx, digest, NULL);
+
+    retval = PyString_FromStringAndSize((const char *)digest, digest_size);
+    EVP_MD_CTX_cleanup(&temp_ctx);
+    return retval;
+}
+
+PyDoc_STRVAR(EVP_hexdigest__doc__,
+"Return the digest value as a string of hexadecimal digits.");
+
+static PyObject *
+EVP_hexdigest(EVPobject *self, PyObject *unused)
+{
+    unsigned char digest[EVP_MAX_MD_SIZE];
+    EVP_MD_CTX temp_ctx;
+    PyObject *retval;
+    char *hex_digest;
+    unsigned int i, j, digest_size;
+
+    /* Get the raw (binary) digest value */
+    EVP_MD_CTX_copy(&temp_ctx, &self->ctx);
+    digest_size = EVP_MD_CTX_size(&temp_ctx);
+    EVP_DigestFinal(&temp_ctx, digest, NULL);
+
+    EVP_MD_CTX_cleanup(&temp_ctx);
+
+    /* Create a new string */
+    /* NOTE: not thread safe! modifying an already created string object */
+    /* (not a problem because we hold the GIL by default) */
+    retval = PyString_FromStringAndSize(NULL, digest_size * 2);
+    if (!retval)
+	    return NULL;
+    hex_digest = PyString_AsString(retval);
+    if (!hex_digest) {
+	    Py_DECREF(retval);
+	    return NULL;
+    }
+
+    /* Make hex version of the digest */
+    for(i=j=0; i<digest_size; i++) {
+        char c;
+        c = (digest[i] >> 4) & 0xf;
+	c = (c>9) ? c+'a'-10 : c + '0';
+        hex_digest[j++] = c;
+        c = (digest[i] & 0xf);
+	c = (c>9) ? c+'a'-10 : c + '0';
+        hex_digest[j++] = c;
+    }
+    return retval;
+}
+
+PyDoc_STRVAR(EVP_update__doc__,
+"Update this hash object's state with the provided string.");
+
+static PyObject *
+EVP_update(EVPobject *self, PyObject *args)
+{
+    unsigned char *cp;
+    Py_ssize_t len;
+
+    if (!PyArg_ParseTuple(args, "s#:update", &cp, &len))
+        return NULL;
+
+    if (len > 0 && len <= MUNCH_SIZE) {
+    EVP_DigestUpdate(&self->ctx, cp, Py_SAFE_DOWNCAST(len, Py_ssize_t,
+                                                      unsigned int));
+    } else {
+        Py_ssize_t offset = 0;
+        while (len) {
+            unsigned int process = len > MUNCH_SIZE ? MUNCH_SIZE : len;
+            EVP_DigestUpdate(&self->ctx, cp + offset, process);
+            len -= process;
+            offset += process;
+        }
+    }
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyMethodDef EVP_methods[] = {
+    {"update",	  (PyCFunction)EVP_update,    METH_VARARGS, EVP_update__doc__},
+    {"digest",	  (PyCFunction)EVP_digest,    METH_NOARGS,  EVP_digest__doc__},
+    {"hexdigest", (PyCFunction)EVP_hexdigest, METH_NOARGS,  EVP_hexdigest__doc__},
+    {"copy",	  (PyCFunction)EVP_copy,      METH_NOARGS,  EVP_copy__doc__},
+    {NULL,	  NULL}		/* sentinel */
+};
+
+static PyObject *
+EVP_get_block_size(EVPobject *self, void *closure)
+{
+    return PyInt_FromLong(EVP_MD_CTX_block_size(&((EVPobject *)self)->ctx));
+}
+
+static PyObject *
+EVP_get_digest_size(EVPobject *self, void *closure)
+{
+    return PyInt_FromLong(EVP_MD_CTX_size(&((EVPobject *)self)->ctx));
+}
+
+static PyMemberDef EVP_members[] = {
+    {"name", T_OBJECT, offsetof(EVPobject, name), READONLY, PyDoc_STR("algorithm name.")},
+    {NULL}  /* Sentinel */
+};
+
+static PyGetSetDef EVP_getseters[] = {
+    {"digest_size",
+     (getter)EVP_get_digest_size, NULL,
+     NULL,
+     NULL},
+    {"block_size",
+     (getter)EVP_get_block_size, NULL,
+     NULL,
+     NULL},
+    /* the old md5 and sha modules support 'digest_size' as in PEP 247.
+     * the old sha module also supported 'digestsize'.  ugh. */
+    {"digestsize",
+     (getter)EVP_get_digest_size, NULL,
+     NULL,
+     NULL},
+    {NULL}  /* Sentinel */
+};
+
+
+static PyObject *
+EVP_repr(PyObject *self)
+{
+    char buf[100];
+    PyOS_snprintf(buf, sizeof(buf), "<%s HASH object @ %p>",
+            PyString_AsString(((EVPobject *)self)->name), self);
+    return PyString_FromString(buf);
+}
+
+#if HASH_OBJ_CONSTRUCTOR
+static int
+EVP_tp_init(EVPobject *self, PyObject *args, PyObject *kwds)
+{
+    static char *kwlist[] = {"name", "string", NULL};
+    PyObject *name_obj = NULL;
+    char *nameStr;
+    unsigned char *cp = NULL;
+    Py_ssize_t len = 0;
+    const EVP_MD *digest;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s#:HASH", kwlist,
+                                     &name_obj, &cp, &len)) {
+        return -1;
+    }
+
+    if (!PyArg_Parse(name_obj, "s", &nameStr)) {
+        PyErr_SetString(PyExc_TypeError, "name must be a string");
+        return -1;
+    }
+
+    digest = EVP_get_digestbyname(nameStr);
+    if (!digest) {
+        PyErr_SetString(PyExc_ValueError, "unknown hash function");
+        return -1;
+    }
+    EVP_DigestInit(&self->ctx, digest);
+
+    self->name = name_obj;
+    Py_INCREF(self->name);
+
+    if (cp && len) {
+        if (len > 0 && len <= MUNCH_SIZE) {
+        EVP_DigestUpdate(&self->ctx, cp, Py_SAFE_DOWNCAST(len, Py_ssize_t,
+                                                          unsigned int));
+        } else {
+            Py_ssize_t offset = 0;
+            while (len) {
+                unsigned int process = len > MUNCH_SIZE ? MUNCH_SIZE : len;
+                EVP_DigestUpdate(&self->ctx, cp + offset, process);
+                len -= process;
+                offset += process;
+            }
+        }
+    }
+    
+    return 0;
+}
+#endif
+
+
+PyDoc_STRVAR(hashtype_doc,
+"A hash represents the object used to calculate a checksum of a\n\
+string of information.\n\
+\n\
+Methods:\n\
+\n\
+update() -- updates the current digest with an additional string\n\
+digest() -- return the current digest value\n\
+hexdigest() -- return the current digest as a string of hexadecimal digits\n\
+copy() -- return a copy of the current hash object\n\
+\n\
+Attributes:\n\
+\n\
+name -- the hash algorithm being used by this object\n\
+digest_size -- number of bytes in this hashes output\n");
+
+static PyTypeObject EVPtype = {
+    PyObject_HEAD_INIT(NULL)
+    0,			/*ob_size*/
+    "_hashlib.HASH",    /*tp_name*/
+    sizeof(EVPobject),	/*tp_basicsize*/
+    0,			/*tp_itemsize*/
+    /* methods */
+    EVP_dealloc,	/*tp_dealloc*/
+    0,			/*tp_print*/
+    0,                  /*tp_getattr*/
+    0,                  /*tp_setattr*/
+    0,                  /*tp_compare*/
+    EVP_repr,           /*tp_repr*/
+    0,                  /*tp_as_number*/
+    0,                  /*tp_as_sequence*/
+    0,                  /*tp_as_mapping*/
+    0,                  /*tp_hash*/
+    0,                  /*tp_call*/
+    0,                  /*tp_str*/
+    0,                  /*tp_getattro*/
+    0,                  /*tp_setattro*/
+    0,                  /*tp_as_buffer*/
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+    hashtype_doc,       /*tp_doc*/
+    0,                  /*tp_traverse*/
+    0,			/*tp_clear*/
+    0,			/*tp_richcompare*/
+    0,			/*tp_weaklistoffset*/
+    0,			/*tp_iter*/
+    0,			/*tp_iternext*/
+    EVP_methods,	/* tp_methods */
+    EVP_members,	/* tp_members */
+    EVP_getseters,      /* tp_getset */
+#if 1
+    0,                  /* tp_base */
+    0,                  /* tp_dict */
+    0,                  /* tp_descr_get */
+    0,                  /* tp_descr_set */
+    0,                  /* tp_dictoffset */
+#endif
+#if HASH_OBJ_CONSTRUCTOR
+    (initproc)EVP_tp_init, /* tp_init */
+#endif
+};
+
+static PyObject *
+EVPnew(PyObject *name_obj,
+       const EVP_MD *digest, const EVP_MD_CTX *initial_ctx,
+       const unsigned char *cp, Py_ssize_t len)
+{
+    EVPobject *self;
+
+    if (!digest && !initial_ctx) {
+        PyErr_SetString(PyExc_ValueError, "unsupported hash type");
+        return NULL;
+    }
+
+    if ((self = newEVPobject(name_obj)) == NULL)
+        return NULL;
+
+    if (initial_ctx) {
+        EVP_MD_CTX_copy(&self->ctx, initial_ctx);
+    } else {
+        EVP_DigestInit(&self->ctx, digest);
+    }
+
+    if (cp && len) {
+        if (len > 0 && len <= MUNCH_SIZE) {
+            EVP_DigestUpdate(&self->ctx, cp, Py_SAFE_DOWNCAST(len, Py_ssize_t,
+                                                              unsigned int));
+        } else {
+            Py_ssize_t offset = 0;
+            while (len) {
+                unsigned int process = len > MUNCH_SIZE ? MUNCH_SIZE : len;
+                EVP_DigestUpdate(&self->ctx, cp + offset, process);
+                len -= process;
+                offset += process;
+            }
+        }
+    }
+
+    return (PyObject *)self;
+}
+
+
+/* The module-level function: new() */
+
+PyDoc_STRVAR(EVP_new__doc__,
+"Return a new hash object using the named algorithm.\n\
+An optional string argument may be provided and will be\n\
+automatically hashed.\n\
+\n\
+The MD5 and SHA1 algorithms are always supported.\n");
+
+static PyObject *
+EVP_new(PyObject *self, PyObject *args, PyObject *kwdict)
+{
+    static char *kwlist[] = {"name", "string", NULL};
+    PyObject *name_obj = NULL;
+    char *name;
+    const EVP_MD *digest;
+    unsigned char *cp = NULL;
+    Py_ssize_t len = 0;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O|s#:new", kwlist,
+                                     &name_obj, &cp, &len)) {
+        return NULL;
+    }
+
+    if (!PyArg_Parse(name_obj, "s", &name)) {
+        PyErr_SetString(PyExc_TypeError, "name must be a string");
+        return NULL;
+    }
+
+    digest = EVP_get_digestbyname(name);
+
+    return EVPnew(name_obj, digest, NULL, cp, len);
+}
+
+/*
+ *  This macro generates constructor function definitions for specific
+ *  hash algorithms.  These constructors are much faster than calling
+ *  the generic one passing it a python string and are noticably
+ *  faster than calling a python new() wrapper.  Thats important for
+ *  code that wants to make hashes of a bunch of small strings.
+ */
+#define GEN_CONSTRUCTOR(NAME)  \
+    static PyObject * \
+    EVP_new_ ## NAME (PyObject *self, PyObject *args) \
+    { \
+        unsigned char *cp = NULL; \
+        Py_ssize_t len = 0; \
+     \
+        if (!PyArg_ParseTuple(args, "|s#:" #NAME , &cp, &len)) { \
+            return NULL; \
+        } \
+     \
+        return EVPnew( \
+                CONST_ ## NAME ## _name_obj, \
+                NULL, \
+                CONST_new_ ## NAME ## _ctx_p, \
+                cp, len); \
+    }
+
+/* a PyMethodDef structure for the constructor */
+#define CONSTRUCTOR_METH_DEF(NAME)  \
+    {"openssl_" #NAME, (PyCFunction)EVP_new_ ## NAME, METH_VARARGS, \
+        PyDoc_STR("Returns a " #NAME \
+                  " hash object; optionally initialized with a string") \
+    }
+
+/* used in the init function to setup a constructor */
+#define INIT_CONSTRUCTOR_CONSTANTS(NAME)  do { \
+    CONST_ ## NAME ## _name_obj = PyString_FromString(#NAME); \
+    if (EVP_get_digestbyname(#NAME)) { \
+        CONST_new_ ## NAME ## _ctx_p = &CONST_new_ ## NAME ## _ctx; \
+        EVP_DigestInit(CONST_new_ ## NAME ## _ctx_p, EVP_get_digestbyname(#NAME)); \
+    } \
+} while (0);
+
+GEN_CONSTRUCTOR(md5)
+GEN_CONSTRUCTOR(sha1)
+GEN_CONSTRUCTOR(sha224)
+GEN_CONSTRUCTOR(sha256)
+GEN_CONSTRUCTOR(sha384)
+GEN_CONSTRUCTOR(sha512)
+
+/* List of functions exported by this module */
+
+static struct PyMethodDef EVP_functions[] = {
+    {"new", (PyCFunction)EVP_new, METH_VARARGS|METH_KEYWORDS, EVP_new__doc__},
+    CONSTRUCTOR_METH_DEF(md5),
+    CONSTRUCTOR_METH_DEF(sha1),
+    CONSTRUCTOR_METH_DEF(sha224),
+    CONSTRUCTOR_METH_DEF(sha256),
+    CONSTRUCTOR_METH_DEF(sha384),
+    CONSTRUCTOR_METH_DEF(sha512),
+    {NULL,	NULL}		 /* Sentinel */
+};
+
+
+/* Initialize this module. */
+
+PyMODINIT_FUNC
+init_hashlib(void)
+{
+    PyObject *m;
+
+    OpenSSL_add_all_digests();
+
+    /* TODO build EVP_functions openssl_* entries dynamically based
+     * on what hashes are supported rather than listing many
+     * but having some be unsupported.  Only init appropriate
+     * constants. */
+
+    EVPtype.ob_type = &PyType_Type;
+    if (PyType_Ready(&EVPtype) < 0)
+        return;
+
+    m = Py_InitModule("_hashlib", EVP_functions);
+    if (m == NULL)
+        return;
+
+#if HASH_OBJ_CONSTRUCTOR
+    Py_INCREF(&EVPtype);
+    PyModule_AddObject(m, "HASH", (PyObject *)&EVPtype);
+#endif
+
+    /* these constants are used by the convenience constructors */
+    INIT_CONSTRUCTOR_CONSTANTS(md5);
+    INIT_CONSTRUCTOR_CONSTANTS(sha1);
+    INIT_CONSTRUCTOR_CONSTANTS(sha224);
+    INIT_CONSTRUCTOR_CONSTANTS(sha256);
+    INIT_CONSTRUCTOR_CONSTANTS(sha384);
+    INIT_CONSTRUCTOR_CONSTANTS(sha512);
+}
diff --git a/src/Modules/md5.c b/src/Modules/md5.c
new file mode 100644
--- /dev/null
+++ b/src/Modules/md5.c
@@ -0,0 +1,381 @@
+/*
+  Copyright (C) 1999, 2000, 2002 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost at aladdin.com
+
+ */
+/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321, whose
+  text is available at
+	http://www.ietf.org/rfc/rfc1321.txt
+  The code is derived from the text of the RFC, including the test suite
+  (section A.5) but excluding the rest of Appendix A.  It does not include
+  any code or documentation that is identified in the RFC as being
+  copyrighted.
+
+  The original and principal author of md5.c is L. Peter Deutsch
+  <ghost at aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+	either statically or dynamically; added missing #include <string.h>
+	in library.
+  2002-03-11 lpd Corrected argument list for main(), and added int return
+	type, in test program and T value program.
+  2002-02-21 lpd Added missing #include <stdio.h> in test program.
+  2000-07-03 lpd Patched to eliminate warnings about "constant is
+	unsigned in ANSI C, signed in traditional"; made test program
+	self-checking.
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+  1999-05-03 lpd Original version.
+ */
+
+#include "md5.h"
+#include <string.h>
+
+#undef BYTE_ORDER	/* 1 = big-endian, -1 = little-endian, 0 = unknown */
+#ifdef ARCH_IS_BIG_ENDIAN
+#  define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#else
+#  define BYTE_ORDER 0
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3    0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6    0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9    0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13    0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16    0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19    0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22    0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25    0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28    0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31    0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35    0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38    0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41    0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44    0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47    0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50    0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53    0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57    0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60    0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63    0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+    md5_word_t
+	a = pms->abcd[0], b = pms->abcd[1],
+	c = pms->abcd[2], d = pms->abcd[3];
+    md5_word_t t;
+#if BYTE_ORDER > 0
+    /* Define storage only for big-endian CPUs. */
+    md5_word_t X[16];
+#else
+    /* Define storage for little-endian or both types of CPUs. */
+    md5_word_t xbuf[16];
+    const md5_word_t *X;
+#endif
+
+    {
+#if BYTE_ORDER == 0
+	/*
+	 * Determine dynamically whether this is a big-endian or
+	 * little-endian machine, since we can use a more efficient
+	 * algorithm on the latter.
+	 */
+	static const int w = 1;
+
+	if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0		/* little-endian */
+	{
+	    /*
+	     * On little-endian machines, we can process properly aligned
+	     * data without copying it.
+	     */
+	    if (!((data - (const md5_byte_t *)0) & 3)) {
+		/* data are properly aligned */
+		X = (const md5_word_t *)data;
+	    } else {
+		/* not aligned */
+		memcpy(xbuf, data, 64);
+		X = xbuf;
+	    }
+	}
+#endif
+#if BYTE_ORDER == 0
+	else			/* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0		/* big-endian */
+	{
+	    /*
+	     * On big-endian machines, we must arrange the bytes in the
+	     * right order.
+	     */
+	    const md5_byte_t *xp = data;
+	    int i;
+
+#  if BYTE_ORDER == 0
+	    X = xbuf;		/* (dynamic only) */
+#  else
+#    define xbuf X		/* (static only) */
+#  endif
+	    for (i = 0; i < 16; ++i, xp += 4)
+		xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+	}
+#endif
+    }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+    /* Round 1. */
+    /* Let [abcd k s i] denote the operation
+       a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + F(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+    /* Do the following 16 operations. */
+    SET(a, b, c, d,  0,  7,  T1);
+    SET(d, a, b, c,  1, 12,  T2);
+    SET(c, d, a, b,  2, 17,  T3);
+    SET(b, c, d, a,  3, 22,  T4);
+    SET(a, b, c, d,  4,  7,  T5);
+    SET(d, a, b, c,  5, 12,  T6);
+    SET(c, d, a, b,  6, 17,  T7);
+    SET(b, c, d, a,  7, 22,  T8);
+    SET(a, b, c, d,  8,  7,  T9);
+    SET(d, a, b, c,  9, 12, T10);
+    SET(c, d, a, b, 10, 17, T11);
+    SET(b, c, d, a, 11, 22, T12);
+    SET(a, b, c, d, 12,  7, T13);
+    SET(d, a, b, c, 13, 12, T14);
+    SET(c, d, a, b, 14, 17, T15);
+    SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+     /* Round 2. */
+     /* Let [abcd k s i] denote the operation
+          a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + G(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  1,  5, T17);
+    SET(d, a, b, c,  6,  9, T18);
+    SET(c, d, a, b, 11, 14, T19);
+    SET(b, c, d, a,  0, 20, T20);
+    SET(a, b, c, d,  5,  5, T21);
+    SET(d, a, b, c, 10,  9, T22);
+    SET(c, d, a, b, 15, 14, T23);
+    SET(b, c, d, a,  4, 20, T24);
+    SET(a, b, c, d,  9,  5, T25);
+    SET(d, a, b, c, 14,  9, T26);
+    SET(c, d, a, b,  3, 14, T27);
+    SET(b, c, d, a,  8, 20, T28);
+    SET(a, b, c, d, 13,  5, T29);
+    SET(d, a, b, c,  2,  9, T30);
+    SET(c, d, a, b,  7, 14, T31);
+    SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+     /* Round 3. */
+     /* Let [abcd k s t] denote the operation
+          a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + H(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  5,  4, T33);
+    SET(d, a, b, c,  8, 11, T34);
+    SET(c, d, a, b, 11, 16, T35);
+    SET(b, c, d, a, 14, 23, T36);
+    SET(a, b, c, d,  1,  4, T37);
+    SET(d, a, b, c,  4, 11, T38);
+    SET(c, d, a, b,  7, 16, T39);
+    SET(b, c, d, a, 10, 23, T40);
+    SET(a, b, c, d, 13,  4, T41);
+    SET(d, a, b, c,  0, 11, T42);
+    SET(c, d, a, b,  3, 16, T43);
+    SET(b, c, d, a,  6, 23, T44);
+    SET(a, b, c, d,  9,  4, T45);
+    SET(d, a, b, c, 12, 11, T46);
+    SET(c, d, a, b, 15, 16, T47);
+    SET(b, c, d, a,  2, 23, T48);
+#undef SET
+
+     /* Round 4. */
+     /* Let [abcd k s t] denote the operation
+          a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + I(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  0,  6, T49);
+    SET(d, a, b, c,  7, 10, T50);
+    SET(c, d, a, b, 14, 15, T51);
+    SET(b, c, d, a,  5, 21, T52);
+    SET(a, b, c, d, 12,  6, T53);
+    SET(d, a, b, c,  3, 10, T54);
+    SET(c, d, a, b, 10, 15, T55);
+    SET(b, c, d, a,  1, 21, T56);
+    SET(a, b, c, d,  8,  6, T57);
+    SET(d, a, b, c, 15, 10, T58);
+    SET(c, d, a, b,  6, 15, T59);
+    SET(b, c, d, a, 13, 21, T60);
+    SET(a, b, c, d,  4,  6, T61);
+    SET(d, a, b, c, 11, 10, T62);
+    SET(c, d, a, b,  2, 15, T63);
+    SET(b, c, d, a,  9, 21, T64);
+#undef SET
+
+     /* Then perform the following additions. (That is increment each
+        of the four registers by the value it had before this block
+        was started.) */
+    pms->abcd[0] += a;
+    pms->abcd[1] += b;
+    pms->abcd[2] += c;
+    pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+    pms->count[0] = pms->count[1] = 0;
+    pms->abcd[0] = 0x67452301;
+    pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+    pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+    pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+    const md5_byte_t *p = data;
+    int left = nbytes;
+    int offset = (pms->count[0] >> 3) & 63;
+    md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+    if (nbytes <= 0)
+	return;
+
+    /* Update the message length. */
+    pms->count[1] += nbytes >> 29;
+    pms->count[0] += nbits;
+    if (pms->count[0] < nbits)
+	pms->count[1]++;
+
+    /* Process an initial partial block. */
+    if (offset) {
+	int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+	memcpy(pms->buf + offset, p, copy);
+	if (offset + copy < 64)
+	    return;
+	p += copy;
+	left -= copy;
+	md5_process(pms, pms->buf);
+    }
+
+    /* Process full blocks. */
+    for (; left >= 64; p += 64, left -= 64)
+	md5_process(pms, p);
+
+    /* Process a final partial block. */
+    if (left)
+	memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+    static const md5_byte_t pad[64] = {
+	0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    };
+    md5_byte_t data[8];
+    int i;
+
+    /* Save the length before padding. */
+    for (i = 0; i < 8; ++i)
+	data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+    /* Pad to 56 bytes mod 64. */
+    md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+    /* Append the length. */
+    md5_append(pms, data, 8);
+    for (i = 0; i < 16; ++i)
+	digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
diff --git a/src/Modules/md5.h b/src/Modules/md5.h
new file mode 100644
--- /dev/null
+++ b/src/Modules/md5.h
@@ -0,0 +1,91 @@
+/*
+  Copyright (C) 1999, 2002 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost at aladdin.com
+
+ */
+/* $Id: md5.h 43594 2006-04-03 16:27:50Z matthias.klose $ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321, whose
+  text is available at
+	http://www.ietf.org/rfc/rfc1321.txt
+  The code is derived from the text of the RFC, including the test suite
+  (section A.5) but excluding the rest of Appendix A.  It does not include
+  any code or documentation that is identified in the RFC as being
+  copyrighted.
+
+  The original and principal author of md5.h is L. Peter Deutsch
+  <ghost at aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2002-04-13 lpd Removed support for non-ANSI compilers; removed
+	references to Ghostscript; clarified derivation from RFC 1321;
+	now handles byte order either statically or dynamically.
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+	added conditionalization for C++ compilation from Martin
+	Purschke <purschke at bnl.gov>.
+  1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+#  define md5_INCLUDED
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order.  If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+    md5_word_t count[2];	/* message length in bits, lsw first */
+    md5_word_t abcd[4];		/* digest buffer */
+    md5_byte_t buf[64];		/* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C" 
+{
+#endif
+
+/* Initialize the algorithm. */
+void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#ifdef __cplusplus
+}  /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
diff --git a/src/Modules/md5module.c b/src/Modules/md5module.c
new file mode 100644
--- /dev/null
+++ b/src/Modules/md5module.c
@@ -0,0 +1,312 @@
+
+/* MD5 module */
+
+/* This module provides an interface to the RSA Data Security,
+   Inc. MD5 Message-Digest Algorithm, described in RFC 1321.
+   It requires the files md5c.c and md5.h (which are slightly changed
+   from the versions in the RFC to avoid the "global.h" file.) */
+
+
+/* MD5 objects */
+
+#include "Python.h"
+#include "structmember.h"
+#include "md5.h"
+
+typedef struct {
+	PyObject_HEAD
+        md5_state_t	md5;		/* the context holder */
+} md5object;
+
+static PyTypeObject MD5type;
+
+#define is_md5object(v)		((v)->ob_type == &MD5type)
+
+static md5object *
+newmd5object(void)
+{
+	md5object *md5p;
+
+	md5p = PyObject_New(md5object, &MD5type);
+	if (md5p == NULL)
+		return NULL;
+
+	md5_init(&md5p->md5);	/* actual initialisation */
+	return md5p;
+}
+
+
+/* MD5 methods */
+
+static void
+md5_dealloc(md5object *md5p)
+{
+	PyObject_Del(md5p);
+}
+
+
+/* MD5 methods-as-attributes */
+
+static PyObject *
+md5_update(md5object *self, PyObject *args)
+{
+	unsigned char *cp;
+	int len;
+
+	if (!PyArg_ParseTuple(args, "s#:update", &cp, &len))
+		return NULL;
+
+	md5_append(&self->md5, cp, len);
+
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
+PyDoc_STRVAR(update_doc,
+"update (arg)\n\
+\n\
+Update the md5 object with the string arg. Repeated calls are\n\
+equivalent to a single call with the concatenation of all the\n\
+arguments.");
+
+
+static PyObject *
+md5_digest(md5object *self)
+{
+ 	md5_state_t mdContext;
+	unsigned char aDigest[16];
+
+	/* make a temporary copy, and perform the final */
+	mdContext = self->md5;
+	md5_finish(&mdContext, aDigest);
+
+	return PyString_FromStringAndSize((char *)aDigest, 16);
+}
+
+PyDoc_STRVAR(digest_doc,
+"digest() -> string\n\
+\n\
+Return the digest of the strings passed to the update() method so\n\
+far. This is a 16-byte string which may contain non-ASCII characters,\n\
+including null bytes.");
+
+
+static PyObject *
+md5_hexdigest(md5object *self)
+{
+ 	md5_state_t mdContext;
+	unsigned char digest[16];
+	unsigned char hexdigest[32];
+	int i, j;
+
+	/* make a temporary copy, and perform the final */
+	mdContext = self->md5;
+	md5_finish(&mdContext, digest);
+
+	/* Make hex version of the digest */
+	for(i=j=0; i<16; i++) {
+		char c;
+		c = (digest[i] >> 4) & 0xf;
+		c = (c>9) ? c+'a'-10 : c + '0';
+		hexdigest[j++] = c;
+		c = (digest[i] & 0xf);
+		c = (c>9) ? c+'a'-10 : c + '0';
+		hexdigest[j++] = c;
+	}
+	return PyString_FromStringAndSize((char*)hexdigest, 32);
+}
+
+
+PyDoc_STRVAR(hexdigest_doc,
+"hexdigest() -> string\n\
+\n\
+Like digest(), but returns the digest as a string of hexadecimal digits.");
+
+
+static PyObject *
+md5_copy(md5object *self)
+{
+	md5object *md5p;
+
+	if ((md5p = newmd5object()) == NULL)
+		return NULL;
+
+	md5p->md5 = self->md5;
+
+	return (PyObject *)md5p;
+}
+
+PyDoc_STRVAR(copy_doc,
+"copy() -> md5 object\n\
+\n\
+Return a copy (``clone'') of the md5 object.");
+
+
+static PyMethodDef md5_methods[] = {
+	{"update",    (PyCFunction)md5_update,    METH_VARARGS, update_doc},
+	{"digest",    (PyCFunction)md5_digest,    METH_NOARGS,  digest_doc},
+	{"hexdigest", (PyCFunction)md5_hexdigest, METH_NOARGS,  hexdigest_doc},
+	{"copy",      (PyCFunction)md5_copy,      METH_NOARGS,  copy_doc},
+	{NULL, NULL}			     /* sentinel */
+};
+
+static PyObject *
+md5_get_block_size(PyObject *self, void *closure)
+{
+    return PyInt_FromLong(64);
+}
+
+static PyObject *
+md5_get_digest_size(PyObject *self, void *closure)
+{
+    return PyInt_FromLong(16);
+}
+
+static PyObject *
+md5_get_name(PyObject *self, void *closure)
+{
+    return PyString_FromStringAndSize("MD5", 3);
+}
+
+static PyGetSetDef md5_getseters[] = {
+    {"digest_size",
+     (getter)md5_get_digest_size, NULL,
+     NULL,
+     NULL},
+    {"block_size",
+     (getter)md5_get_block_size, NULL,
+     NULL,
+     NULL},
+    {"name",
+     (getter)md5_get_name, NULL,
+     NULL,
+     NULL},
+    /* the old md5 and sha modules support 'digest_size' as in PEP 247.
+     * the old sha module also supported 'digestsize'.  ugh. */
+    {"digestsize",
+     (getter)md5_get_digest_size, NULL,
+     NULL,
+     NULL},
+    {NULL}  /* Sentinel */
+};
+
+
+PyDoc_STRVAR(module_doc,
+"This module implements the interface to RSA's MD5 message digest\n\
+algorithm (see also Internet RFC 1321). Its use is quite\n\
+straightforward: use the new() to create an md5 object. You can now\n\
+feed this object with arbitrary strings using the update() method, and\n\
+at any point you can ask it for the digest (a strong kind of 128-bit\n\
+checksum, a.k.a. ``fingerprint'') of the concatenation of the strings\n\
+fed to it so far using the digest() method.\n\
+\n\
+Functions:\n\
+\n\
+new([arg]) -- return a new md5 object, initialized with arg if provided\n\
+md5([arg]) -- DEPRECATED, same as new, but for compatibility\n\
+\n\
+Special Objects:\n\
+\n\
+MD5Type -- type object for md5 objects");
+
+PyDoc_STRVAR(md5type_doc,
+"An md5 represents the object used to calculate the MD5 checksum of a\n\
+string of information.\n\
+\n\
+Methods:\n\
+\n\
+update() -- updates the current digest with an additional string\n\
+digest() -- return the current digest value\n\
+hexdigest() -- return the current digest as a string of hexadecimal digits\n\
+copy() -- return a copy of the current md5 object");
+
+static PyTypeObject MD5type = {
+	PyObject_HEAD_INIT(NULL)
+	0,			  /*ob_size*/
+	"_md5.md5",		  /*tp_name*/
+	sizeof(md5object),	  /*tp_size*/
+	0,			  /*tp_itemsize*/
+	/* methods */
+	(destructor)md5_dealloc,  /*tp_dealloc*/
+	0,			  /*tp_print*/
+	0,                        /*tp_getattr*/
+	0,			  /*tp_setattr*/
+	0,			  /*tp_compare*/
+	0,			  /*tp_repr*/
+        0,			  /*tp_as_number*/
+	0,                        /*tp_as_sequence*/
+	0,			  /*tp_as_mapping*/
+	0, 			  /*tp_hash*/
+	0,			  /*tp_call*/
+	0,			  /*tp_str*/
+	0,			  /*tp_getattro*/
+	0,			  /*tp_setattro*/
+	0,	                  /*tp_as_buffer*/
+	Py_TPFLAGS_DEFAULT,	  /*tp_flags*/
+	md5type_doc,		  /*tp_doc*/
+        0,                        /*tp_traverse*/
+        0,			  /*tp_clear*/
+        0,			  /*tp_richcompare*/
+        0,			  /*tp_weaklistoffset*/
+        0,			  /*tp_iter*/
+        0,			  /*tp_iternext*/
+        md5_methods,	          /*tp_methods*/
+        0,      	          /*tp_members*/
+        md5_getseters,            /*tp_getset*/
+};
+
+
+/* MD5 functions */
+
+static PyObject *
+MD5_new(PyObject *self, PyObject *args)
+{
+	md5object *md5p;
+	unsigned char *cp = NULL;
+	int len = 0;
+
+	if (!PyArg_ParseTuple(args, "|s#:new", &cp, &len))
+		return NULL;
+
+	if ((md5p = newmd5object()) == NULL)
+		return NULL;
+
+	if (cp)
+		md5_append(&md5p->md5, cp, len);
+
+	return (PyObject *)md5p;
+}
+
+PyDoc_STRVAR(new_doc,
+"new([arg]) -> md5 object\n\
+\n\
+Return a new md5 object. If arg is present, the method call update(arg)\n\
+is made.");
+
+
+/* List of functions exported by this module */
+
+static PyMethodDef md5_functions[] = {
+	{"new",		(PyCFunction)MD5_new, METH_VARARGS, new_doc},
+	{NULL,		NULL}	/* Sentinel */
+};
+
+
+/* Initialize this module. */
+
+PyMODINIT_FUNC
+init_md5(void)
+{
+	PyObject *m, *d;
+
+        MD5type.ob_type = &PyType_Type;
+        if (PyType_Ready(&MD5type) < 0)
+            return;
+	m = Py_InitModule3("_md5", md5_functions, module_doc);
+	if (m == NULL)
+	    return;
+	d = PyModule_GetDict(m);
+	PyDict_SetItemString(d, "MD5Type", (PyObject *)&MD5type);
+	PyModule_AddIntConstant(m, "digest_size", 16);
+	/* No need to check the error here, the caller will do that */
+}
diff --git a/src/Modules/sha256module.c b/src/Modules/sha256module.c
new file mode 100644
--- /dev/null
+++ b/src/Modules/sha256module.c
@@ -0,0 +1,701 @@
+/* SHA256 module */
+
+/* This module provides an interface to NIST's SHA-256 and SHA-224 Algorithms */
+
+/* See below for information about the original code this module was
+   based upon. Additional work performed by:
+
+   Andrew Kuchling (amk at amk.ca)
+   Greg Stein (gstein at lyra.org)
+   Trevor Perrin (trevp at trevp.net)
+
+   Copyright (C) 2005   Gregory P. Smith (greg at krypto.org)
+   Licensed to PSF under a Contributor Agreement.
+
+*/
+
+/* SHA objects */
+
+#include "Python.h"
+#include "structmember.h"
+
+
+/* Endianness testing and definitions */
+#define TestEndianness(variable) {int i=1; variable=PCT_BIG_ENDIAN;\
+	if (*((char*)&i)==1) variable=PCT_LITTLE_ENDIAN;}
+
+#define PCT_LITTLE_ENDIAN 1
+#define PCT_BIG_ENDIAN 0
+
+/* Some useful types */
+
+typedef unsigned char SHA_BYTE;
+
+#if SIZEOF_INT == 4
+typedef unsigned int SHA_INT32;	/* 32-bit integer */
+#else
+/* not defined. compilation will die. */
+#endif
+
+/* The SHA block size and message digest sizes, in bytes */
+
+#define SHA_BLOCKSIZE    64
+#define SHA_DIGESTSIZE  32
+
+/* The structure for storing SHA info */
+
+typedef struct {
+    PyObject_HEAD
+    SHA_INT32 digest[8];		/* Message digest */
+    SHA_INT32 count_lo, count_hi;	/* 64-bit bit count */
+    SHA_BYTE data[SHA_BLOCKSIZE];	/* SHA data buffer */
+    int Endianness;
+    int local;				/* unprocessed amount in data */
+    int digestsize;
+} SHAobject;
+
+/* When run on a little-endian CPU we need to perform byte reversal on an
+   array of longwords. */
+
+static void longReverse(SHA_INT32 *buffer, int byteCount, int Endianness)
+{
+    SHA_INT32 value;
+
+    if ( Endianness == PCT_BIG_ENDIAN )
+	return;
+
+    byteCount /= sizeof(*buffer);
+    while (byteCount--) {
+        value = *buffer;
+        value = ( ( value & 0xFF00FF00L ) >> 8  ) | \
+                ( ( value & 0x00FF00FFL ) << 8 );
+        *buffer++ = ( value << 16 ) | ( value >> 16 );
+    }
+}
+
+static void SHAcopy(SHAobject *src, SHAobject *dest)
+{
+    dest->Endianness = src->Endianness;
+    dest->local = src->local;
+    dest->digestsize = src->digestsize;
+    dest->count_lo = src->count_lo;
+    dest->count_hi = src->count_hi;
+    memcpy(dest->digest, src->digest, sizeof(src->digest));
+    memcpy(dest->data, src->data, sizeof(src->data));
+}
+
+
+/* ------------------------------------------------------------------------
+ *
+ * This code for the SHA-256 algorithm was noted as public domain. The
+ * original headers are pasted below.
+ *
+ * Several changes have been made to make it more compatible with the
+ * Python environment and desired interface.
+ *
+ */
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * gurantee it works.
+ *
+ * Tom St Denis, tomstdenis at iahu.ca, http://libtomcrypt.org
+ */
+
+
+/* SHA256 by Tom St Denis */
+
+/* Various logical functions */
+#define ROR(x, y)\
+( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | \
+((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL)
+#define Ch(x,y,z)       (z ^ (x & (y ^ z)))
+#define Maj(x,y,z)      (((x | y) & z) | (x & y)) 
+#define S(x, n)         ROR((x),(n))
+#define R(x, n)         (((x)&0xFFFFFFFFUL)>>(n))
+#define Sigma0(x)       (S(x, 2) ^ S(x, 13) ^ S(x, 22))
+#define Sigma1(x)       (S(x, 6) ^ S(x, 11) ^ S(x, 25))
+#define Gamma0(x)       (S(x, 7) ^ S(x, 18) ^ R(x, 3))
+#define Gamma1(x)       (S(x, 17) ^ S(x, 19) ^ R(x, 10))
+
+
+static void
+sha_transform(SHAobject *sha_info)
+{
+    int i;
+	SHA_INT32 S[8], W[64], t0, t1;
+
+    memcpy(W, sha_info->data, sizeof(sha_info->data));
+    longReverse(W, (int)sizeof(sha_info->data), sha_info->Endianness);
+
+    for (i = 16; i < 64; ++i) {
+		W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
+    }
+    for (i = 0; i < 8; ++i) {
+        S[i] = sha_info->digest[i];
+    }
+
+    /* Compress */
+#define RND(a,b,c,d,e,f,g,h,i,ki)                    \
+     t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i];   \
+     t1 = Sigma0(a) + Maj(a, b, c);                  \
+     d += t0;                                        \
+     h  = t0 + t1;
+
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x71374491);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcf);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba5);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25b);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b01);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a7);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c1);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc6);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dc);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c8);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf3);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x14292967);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a85);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b2138);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d13);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a7354);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c85);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a1);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664b);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a3);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd6990624);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e3585);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa070);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c08);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774c);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4a);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc70208);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506ceb);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2);
+
+#undef RND     
+    
+    /* feedback */
+    for (i = 0; i < 8; i++) {
+        sha_info->digest[i] = sha_info->digest[i] + S[i];
+    }
+
+}
+
+
+
+/* initialize the SHA digest */
+
+static void
+sha_init(SHAobject *sha_info)
+{
+    TestEndianness(sha_info->Endianness)
+    sha_info->digest[0] = 0x6A09E667L;
+    sha_info->digest[1] = 0xBB67AE85L;
+    sha_info->digest[2] = 0x3C6EF372L;
+    sha_info->digest[3] = 0xA54FF53AL;
+    sha_info->digest[4] = 0x510E527FL;
+    sha_info->digest[5] = 0x9B05688CL;
+    sha_info->digest[6] = 0x1F83D9ABL;
+    sha_info->digest[7] = 0x5BE0CD19L;
+    sha_info->count_lo = 0L;
+    sha_info->count_hi = 0L;
+    sha_info->local = 0;
+    sha_info->digestsize = 32;
+}
+
+static void
+sha224_init(SHAobject *sha_info)
+{
+    TestEndianness(sha_info->Endianness)
+    sha_info->digest[0] = 0xc1059ed8L;
+    sha_info->digest[1] = 0x367cd507L;
+    sha_info->digest[2] = 0x3070dd17L;
+    sha_info->digest[3] = 0xf70e5939L;
+    sha_info->digest[4] = 0xffc00b31L;
+    sha_info->digest[5] = 0x68581511L;
+    sha_info->digest[6] = 0x64f98fa7L;
+    sha_info->digest[7] = 0xbefa4fa4L;
+    sha_info->count_lo = 0L;
+    sha_info->count_hi = 0L;
+    sha_info->local = 0;
+    sha_info->digestsize = 28;
+}
+
+
+/* update the SHA digest */
+
+static void
+sha_update(SHAobject *sha_info, SHA_BYTE *buffer, int count)
+{
+    int i;
+    SHA_INT32 clo;
+
+    clo = sha_info->count_lo + ((SHA_INT32) count << 3);
+    if (clo < sha_info->count_lo) {
+        ++sha_info->count_hi;
+    }
+    sha_info->count_lo = clo;
+    sha_info->count_hi += (SHA_INT32) count >> 29;
+    if (sha_info->local) {
+        i = SHA_BLOCKSIZE - sha_info->local;
+        if (i > count) {
+            i = count;
+        }
+        memcpy(((SHA_BYTE *) sha_info->data) + sha_info->local, buffer, i);
+        count -= i;
+        buffer += i;
+        sha_info->local += i;
+        if (sha_info->local == SHA_BLOCKSIZE) {
+            sha_transform(sha_info);
+        }
+        else {
+            return;
+        }
+    }
+    while (count >= SHA_BLOCKSIZE) {
+        memcpy(sha_info->data, buffer, SHA_BLOCKSIZE);
+        buffer += SHA_BLOCKSIZE;
+        count -= SHA_BLOCKSIZE;
+        sha_transform(sha_info);
+    }
+    memcpy(sha_info->data, buffer, count);
+    sha_info->local = count;
+}
+
+/* finish computing the SHA digest */
+
+static void
+sha_final(unsigned char digest[SHA_DIGESTSIZE], SHAobject *sha_info)
+{
+    int count;
+    SHA_INT32 lo_bit_count, hi_bit_count;
+
+    lo_bit_count = sha_info->count_lo;
+    hi_bit_count = sha_info->count_hi;
+    count = (int) ((lo_bit_count >> 3) & 0x3f);
+    ((SHA_BYTE *) sha_info->data)[count++] = 0x80;
+    if (count > SHA_BLOCKSIZE - 8) {
+	memset(((SHA_BYTE *) sha_info->data) + count, 0,
+	       SHA_BLOCKSIZE - count);
+	sha_transform(sha_info);
+	memset((SHA_BYTE *) sha_info->data, 0, SHA_BLOCKSIZE - 8);
+    }
+    else {
+	memset(((SHA_BYTE *) sha_info->data) + count, 0,
+	       SHA_BLOCKSIZE - 8 - count);
+    }
+
+    /* GJS: note that we add the hi/lo in big-endian. sha_transform will
+       swap these values into host-order. */
+    sha_info->data[56] = (hi_bit_count >> 24) & 0xff;
+    sha_info->data[57] = (hi_bit_count >> 16) & 0xff;
+    sha_info->data[58] = (hi_bit_count >>  8) & 0xff;
+    sha_info->data[59] = (hi_bit_count >>  0) & 0xff;
+    sha_info->data[60] = (lo_bit_count >> 24) & 0xff;
+    sha_info->data[61] = (lo_bit_count >> 16) & 0xff;
+    sha_info->data[62] = (lo_bit_count >>  8) & 0xff;
+    sha_info->data[63] = (lo_bit_count >>  0) & 0xff;
+    sha_transform(sha_info);
+    digest[ 0] = (unsigned char) ((sha_info->digest[0] >> 24) & 0xff);
+    digest[ 1] = (unsigned char) ((sha_info->digest[0] >> 16) & 0xff);
+    digest[ 2] = (unsigned char) ((sha_info->digest[0] >>  8) & 0xff);
+    digest[ 3] = (unsigned char) ((sha_info->digest[0]      ) & 0xff);
+    digest[ 4] = (unsigned char) ((sha_info->digest[1] >> 24) & 0xff);
+    digest[ 5] = (unsigned char) ((sha_info->digest[1] >> 16) & 0xff);
+    digest[ 6] = (unsigned char) ((sha_info->digest[1] >>  8) & 0xff);
+    digest[ 7] = (unsigned char) ((sha_info->digest[1]      ) & 0xff);
+    digest[ 8] = (unsigned char) ((sha_info->digest[2] >> 24) & 0xff);
+    digest[ 9] = (unsigned char) ((sha_info->digest[2] >> 16) & 0xff);
+    digest[10] = (unsigned char) ((sha_info->digest[2] >>  8) & 0xff);
+    digest[11] = (unsigned char) ((sha_info->digest[2]      ) & 0xff);
+    digest[12] = (unsigned char) ((sha_info->digest[3] >> 24) & 0xff);
+    digest[13] = (unsigned char) ((sha_info->digest[3] >> 16) & 0xff);
+    digest[14] = (unsigned char) ((sha_info->digest[3] >>  8) & 0xff);
+    digest[15] = (unsigned char) ((sha_info->digest[3]      ) & 0xff);
+    digest[16] = (unsigned char) ((sha_info->digest[4] >> 24) & 0xff);
+    digest[17] = (unsigned char) ((sha_info->digest[4] >> 16) & 0xff);
+    digest[18] = (unsigned char) ((sha_info->digest[4] >>  8) & 0xff);
+    digest[19] = (unsigned char) ((sha_info->digest[4]      ) & 0xff);
+    digest[20] = (unsigned char) ((sha_info->digest[5] >> 24) & 0xff);
+    digest[21] = (unsigned char) ((sha_info->digest[5] >> 16) & 0xff);
+    digest[22] = (unsigned char) ((sha_info->digest[5] >>  8) & 0xff);
+    digest[23] = (unsigned char) ((sha_info->digest[5]      ) & 0xff);
+    digest[24] = (unsigned char) ((sha_info->digest[6] >> 24) & 0xff);
+    digest[25] = (unsigned char) ((sha_info->digest[6] >> 16) & 0xff);
+    digest[26] = (unsigned char) ((sha_info->digest[6] >>  8) & 0xff);
+    digest[27] = (unsigned char) ((sha_info->digest[6]      ) & 0xff);
+    digest[28] = (unsigned char) ((sha_info->digest[7] >> 24) & 0xff);
+    digest[29] = (unsigned char) ((sha_info->digest[7] >> 16) & 0xff);
+    digest[30] = (unsigned char) ((sha_info->digest[7] >>  8) & 0xff);
+    digest[31] = (unsigned char) ((sha_info->digest[7]      ) & 0xff);
+}
+
+/*
+ * End of copied SHA code.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+static PyTypeObject SHA224type;
+static PyTypeObject SHA256type;
+
+
+static SHAobject *
+newSHA224object(void)
+{
+    return (SHAobject *)PyObject_New(SHAobject, &SHA224type);
+}
+
+static SHAobject *
+newSHA256object(void)
+{
+    return (SHAobject *)PyObject_New(SHAobject, &SHA256type);
+}
+
+/* Internal methods for a hash object */
+
+static void
+SHA_dealloc(PyObject *ptr)
+{
+    PyObject_Del(ptr);
+}
+
+
+/* External methods for a hash object */
+
+PyDoc_STRVAR(SHA256_copy__doc__, "Return a copy of the hash object.");
+
+static PyObject *
+SHA256_copy(SHAobject *self, PyObject *unused)
+{
+    SHAobject *newobj;
+
+    if (((PyObject*)self)->ob_type == &SHA256type) {
+        if ( (newobj = newSHA256object())==NULL)
+            return NULL;
+    } else {
+        if ( (newobj = newSHA224object())==NULL)
+            return NULL;
+    }
+
+    SHAcopy(self, newobj);
+    return (PyObject *)newobj;
+}
+
+PyDoc_STRVAR(SHA256_digest__doc__,
+"Return the digest value as a string of binary data.");
+
+static PyObject *
+SHA256_digest(SHAobject *self, PyObject *unused)
+{
+    unsigned char digest[SHA_DIGESTSIZE];
+    SHAobject temp;
+
+    SHAcopy(self, &temp);
+    sha_final(digest, &temp);
+    return PyString_FromStringAndSize((const char *)digest, self->digestsize);
+}
+
+PyDoc_STRVAR(SHA256_hexdigest__doc__,
+"Return the digest value as a string of hexadecimal digits.");
+
+static PyObject *
+SHA256_hexdigest(SHAobject *self, PyObject *unused)
+{
+    unsigned char digest[SHA_DIGESTSIZE];
+    SHAobject temp;
+    PyObject *retval;
+    char *hex_digest;
+    int i, j;
+
+    /* Get the raw (binary) digest value */
+    SHAcopy(self, &temp);
+    sha_final(digest, &temp);
+
+    /* Create a new string */
+    retval = PyString_FromStringAndSize(NULL, self->digestsize * 2);
+    if (!retval)
+	    return NULL;
+    hex_digest = PyString_AsString(retval);
+    if (!hex_digest) {
+	    Py_DECREF(retval);
+	    return NULL;
+    }
+
+    /* Make hex version of the digest */
+    for(i=j=0; i<self->digestsize; i++) {
+        char c;
+        c = (digest[i] >> 4) & 0xf;
+	c = (c>9) ? c+'a'-10 : c + '0';
+        hex_digest[j++] = c;
+        c = (digest[i] & 0xf);
+	c = (c>9) ? c+'a'-10 : c + '0';
+        hex_digest[j++] = c;
+    }
+    return retval;
+}
+
+PyDoc_STRVAR(SHA256_update__doc__,
+"Update this hash object's state with the provided string.");
+
+static PyObject *
+SHA256_update(SHAobject *self, PyObject *args)
+{
+    unsigned char *cp;
+    int len;
+
+    if (!PyArg_ParseTuple(args, "s#:update", &cp, &len))
+        return NULL;
+
+    sha_update(self, cp, len);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyMethodDef SHA_methods[] = {
+    {"copy",	  (PyCFunction)SHA256_copy,      METH_NOARGS,  SHA256_copy__doc__},
+    {"digest",	  (PyCFunction)SHA256_digest,    METH_NOARGS,  SHA256_digest__doc__},
+    {"hexdigest", (PyCFunction)SHA256_hexdigest, METH_NOARGS,  SHA256_hexdigest__doc__},
+    {"update",	  (PyCFunction)SHA256_update,    METH_VARARGS, SHA256_update__doc__},
+    {NULL,	  NULL}		/* sentinel */
+};
+
+static PyObject *
+SHA256_get_block_size(PyObject *self, void *closure)
+{
+    return PyInt_FromLong(SHA_BLOCKSIZE);
+}
+
+static PyObject *
+SHA256_get_name(PyObject *self, void *closure)
+{
+    if (((SHAobject *)self)->digestsize == 32)
+        return PyString_FromStringAndSize("SHA256", 6);
+    else
+        return PyString_FromStringAndSize("SHA224", 6);
+}
+
+static PyGetSetDef SHA_getseters[] = {
+    {"block_size",
+     (getter)SHA256_get_block_size, NULL,
+     NULL,
+     NULL},
+    {"name",
+     (getter)SHA256_get_name, NULL,
+     NULL,
+     NULL},
+    {NULL}  /* Sentinel */
+};
+
+static PyMemberDef SHA_members[] = {
+    {"digest_size", T_INT, offsetof(SHAobject, digestsize), READONLY, NULL},
+    /* the old md5 and sha modules support 'digest_size' as in PEP 247.
+     * the old sha module also supported 'digestsize'.  ugh. */
+    {"digestsize", T_INT, offsetof(SHAobject, digestsize), READONLY, NULL},
+    {NULL}  /* Sentinel */
+};
+
+static PyTypeObject SHA224type = {
+    PyObject_HEAD_INIT(NULL)
+    0,			/*ob_size*/
+    "_sha256.sha224",	/*tp_name*/
+    sizeof(SHAobject),	/*tp_size*/
+    0,			/*tp_itemsize*/
+    /* methods */
+    SHA_dealloc,	/*tp_dealloc*/
+    0,			/*tp_print*/
+    0,          	/*tp_getattr*/
+    0,                  /*tp_setattr*/
+    0,                  /*tp_compare*/
+    0,                  /*tp_repr*/
+    0,                  /*tp_as_number*/
+    0,                  /*tp_as_sequence*/
+    0,                  /*tp_as_mapping*/
+    0,                  /*tp_hash*/
+    0,                  /*tp_call*/
+    0,                  /*tp_str*/
+    0,                  /*tp_getattro*/
+    0,                  /*tp_setattro*/
+    0,                  /*tp_as_buffer*/
+    Py_TPFLAGS_DEFAULT, /*tp_flags*/
+    0,                  /*tp_doc*/
+    0,                  /*tp_traverse*/
+    0,			/*tp_clear*/
+    0,			/*tp_richcompare*/
+    0,			/*tp_weaklistoffset*/
+    0,			/*tp_iter*/
+    0,			/*tp_iternext*/
+    SHA_methods,	/* tp_methods */
+    SHA_members,	/* tp_members */
+    SHA_getseters,      /* tp_getset */
+};
+
+static PyTypeObject SHA256type = {
+    PyObject_HEAD_INIT(NULL)
+    0,			/*ob_size*/
+    "_sha256.sha256",	/*tp_name*/
+    sizeof(SHAobject),	/*tp_size*/
+    0,			/*tp_itemsize*/
+    /* methods */
+    SHA_dealloc,	/*tp_dealloc*/
+    0,			/*tp_print*/
+    0,          	/*tp_getattr*/
+    0,                  /*tp_setattr*/
+    0,                  /*tp_compare*/
+    0,                  /*tp_repr*/
+    0,                  /*tp_as_number*/
+    0,                  /*tp_as_sequence*/
+    0,                  /*tp_as_mapping*/
+    0,                  /*tp_hash*/
+    0,                  /*tp_call*/
+    0,                  /*tp_str*/
+    0,                  /*tp_getattro*/
+    0,                  /*tp_setattro*/
+    0,                  /*tp_as_buffer*/
+    Py_TPFLAGS_DEFAULT, /*tp_flags*/
+    0,                  /*tp_doc*/
+    0,                  /*tp_traverse*/
+    0,			/*tp_clear*/
+    0,			/*tp_richcompare*/
+    0,			/*tp_weaklistoffset*/
+    0,			/*tp_iter*/
+    0,			/*tp_iternext*/
+    SHA_methods,	/* tp_methods */
+    SHA_members,	/* tp_members */
+    SHA_getseters,      /* tp_getset */
+};
+
+
+/* The single module-level function: new() */
+
+PyDoc_STRVAR(SHA256_new__doc__,
+"Return a new SHA-256 hash object; optionally initialized with a string.");
+
+static PyObject *
+SHA256_new(PyObject *self, PyObject *args, PyObject *kwdict)
+{
+    static char *kwlist[] = {"string", NULL};
+    SHAobject *new;
+    unsigned char *cp = NULL;
+    int len;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|s#:new", kwlist,
+                                     &cp, &len)) {
+        return NULL;
+    }
+
+    if ((new = newSHA256object()) == NULL)
+        return NULL;
+
+    sha_init(new);
+
+    if (PyErr_Occurred()) {
+        Py_DECREF(new);
+        return NULL;
+    }
+    if (cp)
+        sha_update(new, cp, len);
+
+    return (PyObject *)new;
+}
+
+PyDoc_STRVAR(SHA224_new__doc__,
+"Return a new SHA-224 hash object; optionally initialized with a string.");
+
+static PyObject *
+SHA224_new(PyObject *self, PyObject *args, PyObject *kwdict)
+{
+    static char *kwlist[] = {"string", NULL};
+    SHAobject *new;
+    unsigned char *cp = NULL;
+    int len;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|s#:new", kwlist,
+                                     &cp, &len)) {
+        return NULL;
+    }
+
+    if ((new = newSHA224object()) == NULL)
+        return NULL;
+
+    sha224_init(new);
+
+    if (PyErr_Occurred()) {
+        Py_DECREF(new);
+        return NULL;
+    }
+    if (cp)
+        sha_update(new, cp, len);
+
+    return (PyObject *)new;
+}
+
+
+/* List of functions exported by this module */
+
+static struct PyMethodDef SHA_functions[] = {
+    {"sha256", (PyCFunction)SHA256_new, METH_VARARGS|METH_KEYWORDS, SHA256_new__doc__},
+    {"sha224", (PyCFunction)SHA224_new, METH_VARARGS|METH_KEYWORDS, SHA224_new__doc__},
+    {NULL,	NULL}		 /* Sentinel */
+};
+
+
+/* Initialize this module. */
+
+#define insint(n,v) { PyModule_AddIntConstant(m,n,v); }
+
+PyMODINIT_FUNC
+init_sha256(void)
+{
+    PyObject *m;
+
+    SHA224type.ob_type = &PyType_Type;
+    if (PyType_Ready(&SHA224type) < 0)
+        return;
+    SHA256type.ob_type = &PyType_Type;
+    if (PyType_Ready(&SHA256type) < 0)
+        return;
+    m = Py_InitModule("_sha256", SHA_functions);
+    if (m == NULL)
+	return;
+}
diff --git a/src/Modules/sha512module.c b/src/Modules/sha512module.c
new file mode 100644
--- /dev/null
+++ b/src/Modules/sha512module.c
@@ -0,0 +1,769 @@
+/* SHA512 module */
+
+/* This module provides an interface to NIST's SHA-512 and SHA-384 Algorithms */
+
+/* See below for information about the original code this module was
+   based upon. Additional work performed by:
+
+   Andrew Kuchling (amk at amk.ca)
+   Greg Stein (gstein at lyra.org)
+   Trevor Perrin (trevp at trevp.net)
+
+   Copyright (C) 2005   Gregory P. Smith (greg at krypto.org)
+   Licensed to PSF under a Contributor Agreement.
+
+*/
+
+/* SHA objects */
+
+#include "Python.h"
+#include "structmember.h"
+
+#ifdef PY_LONG_LONG /* If no PY_LONG_LONG, don't compile anything! */
+
+/* Endianness testing and definitions */
+#define TestEndianness(variable) {int i=1; variable=PCT_BIG_ENDIAN;\
+	if (*((char*)&i)==1) variable=PCT_LITTLE_ENDIAN;}
+
+#define PCT_LITTLE_ENDIAN 1
+#define PCT_BIG_ENDIAN 0
+
+/* Some useful types */
+
+typedef unsigned char SHA_BYTE;
+
+#if SIZEOF_INT == 4
+typedef unsigned int SHA_INT32;	/* 32-bit integer */
+typedef unsigned PY_LONG_LONG SHA_INT64;	/* 64-bit integer */
+#else
+/* not defined. compilation will die. */
+#endif
+
+/* The SHA block size and message digest sizes, in bytes */
+
+#define SHA_BLOCKSIZE   128
+#define SHA_DIGESTSIZE  64
+
+/* The structure for storing SHA info */
+
+typedef struct {
+    PyObject_HEAD
+    SHA_INT64 digest[8];		/* Message digest */
+    SHA_INT32 count_lo, count_hi;	/* 64-bit bit count */
+    SHA_BYTE data[SHA_BLOCKSIZE];	/* SHA data buffer */
+    int Endianness;
+    int local;				/* unprocessed amount in data */
+    int digestsize;
+} SHAobject;
+
+/* When run on a little-endian CPU we need to perform byte reversal on an
+   array of longwords. */
+
+static void longReverse(SHA_INT64 *buffer, int byteCount, int Endianness)
+{
+    SHA_INT64 value;
+
+    if ( Endianness == PCT_BIG_ENDIAN )
+	return;
+
+    byteCount /= sizeof(*buffer);
+    while (byteCount--) {
+        value = *buffer;
+
+		((unsigned char*)buffer)[0] = (unsigned char)(value >> 56) & 0xff;
+		((unsigned char*)buffer)[1] = (unsigned char)(value >> 48) & 0xff;
+		((unsigned char*)buffer)[2] = (unsigned char)(value >> 40) & 0xff;
+		((unsigned char*)buffer)[3] = (unsigned char)(value >> 32) & 0xff;
+		((unsigned char*)buffer)[4] = (unsigned char)(value >> 24) & 0xff;
+		((unsigned char*)buffer)[5] = (unsigned char)(value >> 16) & 0xff;
+		((unsigned char*)buffer)[6] = (unsigned char)(value >>  8) & 0xff;
+		((unsigned char*)buffer)[7] = (unsigned char)(value      ) & 0xff;
+        
+		buffer++;
+    }
+}
+
+static void SHAcopy(SHAobject *src, SHAobject *dest)
+{
+    dest->Endianness = src->Endianness;
+    dest->local = src->local;
+    dest->digestsize = src->digestsize;
+    dest->count_lo = src->count_lo;
+    dest->count_hi = src->count_hi;
+    memcpy(dest->digest, src->digest, sizeof(src->digest));
+    memcpy(dest->data, src->data, sizeof(src->data));
+}
+
+
+/* ------------------------------------------------------------------------
+ *
+ * This code for the SHA-512 algorithm was noted as public domain. The
+ * original headers are pasted below.
+ *
+ * Several changes have been made to make it more compatible with the
+ * Python environment and desired interface.
+ *
+ */
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * gurantee it works.
+ *
+ * Tom St Denis, tomstdenis at iahu.ca, http://libtomcrypt.org
+ */
+
+
+/* SHA512 by Tom St Denis */
+
+/* Various logical functions */
+#define ROR64(x, y) \
+    ( ((((x) & 0xFFFFFFFFFFFFFFFFULL)>>((unsigned PY_LONG_LONG)(y) & 63)) | \
+      ((x)<<((unsigned PY_LONG_LONG)(64-((y) & 63))))) & 0xFFFFFFFFFFFFFFFFULL)
+#define Ch(x,y,z)       (z ^ (x & (y ^ z)))
+#define Maj(x,y,z)      (((x | y) & z) | (x & y)) 
+#define S(x, n)         ROR64((x),(n))
+#define R(x, n)         (((x) & 0xFFFFFFFFFFFFFFFFULL) >> ((unsigned PY_LONG_LONG)n))
+#define Sigma0(x)       (S(x, 28) ^ S(x, 34) ^ S(x, 39))
+#define Sigma1(x)       (S(x, 14) ^ S(x, 18) ^ S(x, 41))
+#define Gamma0(x)       (S(x, 1) ^ S(x, 8) ^ R(x, 7))
+#define Gamma1(x)       (S(x, 19) ^ S(x, 61) ^ R(x, 6))
+
+
+static void
+sha512_transform(SHAobject *sha_info)
+{
+    int i;
+    SHA_INT64 S[8], W[80], t0, t1;
+
+    memcpy(W, sha_info->data, sizeof(sha_info->data));
+    longReverse(W, (int)sizeof(sha_info->data), sha_info->Endianness);
+
+    for (i = 16; i < 80; ++i) {
+		W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
+    }
+    for (i = 0; i < 8; ++i) {
+        S[i] = sha_info->digest[i];
+    }
+
+    /* Compress */
+#define RND(a,b,c,d,e,f,g,h,i,ki)                    \
+     t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i];   \
+     t1 = Sigma0(a) + Maj(a, b, c);                  \
+     d += t0;                                        \
+     h  = t0 + t1;
+
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98d728ae22ULL);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x7137449123ef65cdULL);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcfec4d3b2fULL);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba58189dbbcULL);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25bf348b538ULL);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1b605d019ULL);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4af194f9bULL);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5da6d8118ULL);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98a3030242ULL);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b0145706fbeULL);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be4ee4b28cULL);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3d5ffb4e2ULL);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74f27b896fULL);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe3b1696b1ULL);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a725c71235ULL);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174cf692694ULL);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c19ef14ad2ULL);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786384f25e3ULL);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc68b8cd5b5ULL);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc77ac9c65ULL);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f592b0275ULL);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa6ea6e483ULL);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dcbd41fbd4ULL);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da831153b5ULL);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152ee66dfabULL);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d2db43210ULL);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c898fb213fULL);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7beef0ee4ULL);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf33da88fc2ULL);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147930aa725ULL);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351e003826fULL);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x142929670a0e6e70ULL);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a8546d22ffcULL);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b21385c26c926ULL);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc5ac42aedULL);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d139d95b3dfULL);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a73548baf63deULL);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb3c77b2a8ULL);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e47edaee6ULL);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c851482353bULL);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a14cf10364ULL);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664bbc423001ULL);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70d0f89791ULL);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a30654be30ULL);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819d6ef5218ULL);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd69906245565a910ULL);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e35855771202aULL);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa07032bbd1b8ULL);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116b8d2d0c8ULL);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c085141ab53ULL);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774cdf8eeb99ULL);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5e19b48a8ULL);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3c5c95a63ULL);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4ae3418acbULL);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f7763e373ULL);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3d6b2b8a3ULL);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee5defb2fcULL);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f43172f60ULL);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814a1f0ab72ULL);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc702081a6439ecULL);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa23631e28ULL);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506cebde82bde9ULL);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7b2c67915ULL);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2e372532bULL);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],64,0xca273eceea26619cULL);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],65,0xd186b8c721c0c207ULL);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],66,0xeada7dd6cde0eb1eULL);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],67,0xf57d4f7fee6ed178ULL);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],68,0x06f067aa72176fbaULL);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],69,0x0a637dc5a2c898a6ULL);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],70,0x113f9804bef90daeULL);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],71,0x1b710b35131c471bULL);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],72,0x28db77f523047d84ULL);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],73,0x32caab7b40c72493ULL);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],74,0x3c9ebe0a15c9bebcULL);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],75,0x431d67c49c100d4cULL);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],76,0x4cc5d4becb3e42b6ULL);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],77,0x597f299cfc657e2aULL);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],78,0x5fcb6fab3ad6faecULL);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],79,0x6c44198c4a475817ULL);
+
+#undef RND     
+    
+    /* feedback */
+    for (i = 0; i < 8; i++) {
+        sha_info->digest[i] = sha_info->digest[i] + S[i];
+    }
+
+}
+
+
+
+/* initialize the SHA digest */
+
+static void
+sha512_init(SHAobject *sha_info)
+{
+    TestEndianness(sha_info->Endianness)
+    sha_info->digest[0] = 0x6a09e667f3bcc908ULL;
+    sha_info->digest[1] = 0xbb67ae8584caa73bULL;
+    sha_info->digest[2] = 0x3c6ef372fe94f82bULL;
+    sha_info->digest[3] = 0xa54ff53a5f1d36f1ULL;
+    sha_info->digest[4] = 0x510e527fade682d1ULL;
+    sha_info->digest[5] = 0x9b05688c2b3e6c1fULL;
+    sha_info->digest[6] = 0x1f83d9abfb41bd6bULL;
+    sha_info->digest[7] = 0x5be0cd19137e2179ULL;
+    sha_info->count_lo = 0L;
+    sha_info->count_hi = 0L;
+    sha_info->local = 0;
+    sha_info->digestsize = 64;
+}
+
+static void
+sha384_init(SHAobject *sha_info)
+{
+    TestEndianness(sha_info->Endianness)
+    sha_info->digest[0] = 0xcbbb9d5dc1059ed8ULL;
+    sha_info->digest[1] = 0x629a292a367cd507ULL;
+    sha_info->digest[2] = 0x9159015a3070dd17ULL;
+    sha_info->digest[3] = 0x152fecd8f70e5939ULL;
+    sha_info->digest[4] = 0x67332667ffc00b31ULL;
+    sha_info->digest[5] = 0x8eb44a8768581511ULL;
+    sha_info->digest[6] = 0xdb0c2e0d64f98fa7ULL;
+    sha_info->digest[7] = 0x47b5481dbefa4fa4ULL;
+    sha_info->count_lo = 0L;
+    sha_info->count_hi = 0L;
+    sha_info->local = 0;
+    sha_info->digestsize = 48;
+}
+
+
+/* update the SHA digest */
+
+static void
+sha512_update(SHAobject *sha_info, SHA_BYTE *buffer, int count)
+{
+    int i;
+    SHA_INT32 clo;
+
+    clo = sha_info->count_lo + ((SHA_INT32) count << 3);
+    if (clo < sha_info->count_lo) {
+        ++sha_info->count_hi;
+    }
+    sha_info->count_lo = clo;
+    sha_info->count_hi += (SHA_INT32) count >> 29;
+    if (sha_info->local) {
+        i = SHA_BLOCKSIZE - sha_info->local;
+        if (i > count) {
+            i = count;
+        }
+        memcpy(((SHA_BYTE *) sha_info->data) + sha_info->local, buffer, i);
+        count -= i;
+        buffer += i;
+        sha_info->local += i;
+        if (sha_info->local == SHA_BLOCKSIZE) {
+            sha512_transform(sha_info);
+        }
+        else {
+            return;
+        }
+    }
+    while (count >= SHA_BLOCKSIZE) {
+        memcpy(sha_info->data, buffer, SHA_BLOCKSIZE);
+        buffer += SHA_BLOCKSIZE;
+        count -= SHA_BLOCKSIZE;
+        sha512_transform(sha_info);
+    }
+    memcpy(sha_info->data, buffer, count);
+    sha_info->local = count;
+}
+
+/* finish computing the SHA digest */
+
+static void
+sha512_final(unsigned char digest[SHA_DIGESTSIZE], SHAobject *sha_info)
+{
+    int count;
+    SHA_INT32 lo_bit_count, hi_bit_count;
+
+    lo_bit_count = sha_info->count_lo;
+    hi_bit_count = sha_info->count_hi;
+    count = (int) ((lo_bit_count >> 3) & 0x7f);
+    ((SHA_BYTE *) sha_info->data)[count++] = 0x80;
+    if (count > SHA_BLOCKSIZE - 16) {
+	memset(((SHA_BYTE *) sha_info->data) + count, 0,
+	       SHA_BLOCKSIZE - count);
+	sha512_transform(sha_info);
+	memset((SHA_BYTE *) sha_info->data, 0, SHA_BLOCKSIZE - 16);
+    }
+    else {
+	memset(((SHA_BYTE *) sha_info->data) + count, 0,
+	       SHA_BLOCKSIZE - 16 - count);
+    }
+
+    /* GJS: note that we add the hi/lo in big-endian. sha512_transform will
+       swap these values into host-order. */
+    sha_info->data[112] = 0;
+    sha_info->data[113] = 0;
+    sha_info->data[114] = 0;
+    sha_info->data[115] = 0;
+    sha_info->data[116] = 0;
+    sha_info->data[117] = 0;
+    sha_info->data[118] = 0;
+    sha_info->data[119] = 0;
+    sha_info->data[120] = (hi_bit_count >> 24) & 0xff;
+    sha_info->data[121] = (hi_bit_count >> 16) & 0xff;
+    sha_info->data[122] = (hi_bit_count >>  8) & 0xff;
+    sha_info->data[123] = (hi_bit_count >>  0) & 0xff;
+    sha_info->data[124] = (lo_bit_count >> 24) & 0xff;
+    sha_info->data[125] = (lo_bit_count >> 16) & 0xff;
+    sha_info->data[126] = (lo_bit_count >>  8) & 0xff;
+    sha_info->data[127] = (lo_bit_count >>  0) & 0xff;
+    sha512_transform(sha_info);
+    digest[ 0] = (unsigned char) ((sha_info->digest[0] >> 56) & 0xff);
+    digest[ 1] = (unsigned char) ((sha_info->digest[0] >> 48) & 0xff);
+    digest[ 2] = (unsigned char) ((sha_info->digest[0] >> 40) & 0xff);
+    digest[ 3] = (unsigned char) ((sha_info->digest[0] >> 32) & 0xff);
+    digest[ 4] = (unsigned char) ((sha_info->digest[0] >> 24) & 0xff);
+    digest[ 5] = (unsigned char) ((sha_info->digest[0] >> 16) & 0xff);
+    digest[ 6] = (unsigned char) ((sha_info->digest[0] >>  8) & 0xff);
+    digest[ 7] = (unsigned char) ((sha_info->digest[0]      ) & 0xff);
+    digest[ 8] = (unsigned char) ((sha_info->digest[1] >> 56) & 0xff);
+    digest[ 9] = (unsigned char) ((sha_info->digest[1] >> 48) & 0xff);
+    digest[10] = (unsigned char) ((sha_info->digest[1] >> 40) & 0xff);
+    digest[11] = (unsigned char) ((sha_info->digest[1] >> 32) & 0xff);
+    digest[12] = (unsigned char) ((sha_info->digest[1] >> 24) & 0xff);
+    digest[13] = (unsigned char) ((sha_info->digest[1] >> 16) & 0xff);
+    digest[14] = (unsigned char) ((sha_info->digest[1] >>  8) & 0xff);
+    digest[15] = (unsigned char) ((sha_info->digest[1]      ) & 0xff);
+    digest[16] = (unsigned char) ((sha_info->digest[2] >> 56) & 0xff);
+    digest[17] = (unsigned char) ((sha_info->digest[2] >> 48) & 0xff);
+    digest[18] = (unsigned char) ((sha_info->digest[2] >> 40) & 0xff);
+    digest[19] = (unsigned char) ((sha_info->digest[2] >> 32) & 0xff);
+    digest[20] = (unsigned char) ((sha_info->digest[2] >> 24) & 0xff);
+    digest[21] = (unsigned char) ((sha_info->digest[2] >> 16) & 0xff);
+    digest[22] = (unsigned char) ((sha_info->digest[2] >>  8) & 0xff);
+    digest[23] = (unsigned char) ((sha_info->digest[2]      ) & 0xff);
+    digest[24] = (unsigned char) ((sha_info->digest[3] >> 56) & 0xff);
+    digest[25] = (unsigned char) ((sha_info->digest[3] >> 48) & 0xff);
+    digest[26] = (unsigned char) ((sha_info->digest[3] >> 40) & 0xff);
+    digest[27] = (unsigned char) ((sha_info->digest[3] >> 32) & 0xff);
+    digest[28] = (unsigned char) ((sha_info->digest[3] >> 24) & 0xff);
+    digest[29] = (unsigned char) ((sha_info->digest[3] >> 16) & 0xff);
+    digest[30] = (unsigned char) ((sha_info->digest[3] >>  8) & 0xff);
+    digest[31] = (unsigned char) ((sha_info->digest[3]      ) & 0xff);
+    digest[32] = (unsigned char) ((sha_info->digest[4] >> 56) & 0xff);
+    digest[33] = (unsigned char) ((sha_info->digest[4] >> 48) & 0xff);
+    digest[34] = (unsigned char) ((sha_info->digest[4] >> 40) & 0xff);
+    digest[35] = (unsigned char) ((sha_info->digest[4] >> 32) & 0xff);
+    digest[36] = (unsigned char) ((sha_info->digest[4] >> 24) & 0xff);
+    digest[37] = (unsigned char) ((sha_info->digest[4] >> 16) & 0xff);
+    digest[38] = (unsigned char) ((sha_info->digest[4] >>  8) & 0xff);
+    digest[39] = (unsigned char) ((sha_info->digest[4]      ) & 0xff);
+    digest[40] = (unsigned char) ((sha_info->digest[5] >> 56) & 0xff);
+    digest[41] = (unsigned char) ((sha_info->digest[5] >> 48) & 0xff);
+    digest[42] = (unsigned char) ((sha_info->digest[5] >> 40) & 0xff);
+    digest[43] = (unsigned char) ((sha_info->digest[5] >> 32) & 0xff);
+    digest[44] = (unsigned char) ((sha_info->digest[5] >> 24) & 0xff);
+    digest[45] = (unsigned char) ((sha_info->digest[5] >> 16) & 0xff);
+    digest[46] = (unsigned char) ((sha_info->digest[5] >>  8) & 0xff);
+    digest[47] = (unsigned char) ((sha_info->digest[5]      ) & 0xff);
+    digest[48] = (unsigned char) ((sha_info->digest[6] >> 56) & 0xff);
+    digest[49] = (unsigned char) ((sha_info->digest[6] >> 48) & 0xff);
+    digest[50] = (unsigned char) ((sha_info->digest[6] >> 40) & 0xff);
+    digest[51] = (unsigned char) ((sha_info->digest[6] >> 32) & 0xff);
+    digest[52] = (unsigned char) ((sha_info->digest[6] >> 24) & 0xff);
+    digest[53] = (unsigned char) ((sha_info->digest[6] >> 16) & 0xff);
+    digest[54] = (unsigned char) ((sha_info->digest[6] >>  8) & 0xff);
+    digest[55] = (unsigned char) ((sha_info->digest[6]      ) & 0xff);
+    digest[56] = (unsigned char) ((sha_info->digest[7] >> 56) & 0xff);
+    digest[57] = (unsigned char) ((sha_info->digest[7] >> 48) & 0xff);
+    digest[58] = (unsigned char) ((sha_info->digest[7] >> 40) & 0xff);
+    digest[59] = (unsigned char) ((sha_info->digest[7] >> 32) & 0xff);
+    digest[60] = (unsigned char) ((sha_info->digest[7] >> 24) & 0xff);
+    digest[61] = (unsigned char) ((sha_info->digest[7] >> 16) & 0xff);
+    digest[62] = (unsigned char) ((sha_info->digest[7] >>  8) & 0xff);
+    digest[63] = (unsigned char) ((sha_info->digest[7]      ) & 0xff);
+}
+
+/*
+ * End of copied SHA code.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+static PyTypeObject SHA384type;
+static PyTypeObject SHA512type;
+
+
+static SHAobject *
+newSHA384object(void)
+{
+    return (SHAobject *)PyObject_New(SHAobject, &SHA384type);
+}
+
+static SHAobject *
+newSHA512object(void)
+{
+    return (SHAobject *)PyObject_New(SHAobject, &SHA512type);
+}
+
+/* Internal methods for a hash object */
+
+static void
+SHA512_dealloc(PyObject *ptr)
+{
+    PyObject_Del(ptr);
+}
+
+
+/* External methods for a hash object */
+
+PyDoc_STRVAR(SHA512_copy__doc__, "Return a copy of the hash object.");
+
+static PyObject *
+SHA512_copy(SHAobject *self, PyObject *unused)
+{
+    SHAobject *newobj;
+
+    if (((PyObject*)self)->ob_type == &SHA512type) {
+        if ( (newobj = newSHA512object())==NULL)
+            return NULL;
+    } else {
+        if ( (newobj = newSHA384object())==NULL)
+            return NULL;
+    }
+
+    SHAcopy(self, newobj);
+    return (PyObject *)newobj;
+}
+
+PyDoc_STRVAR(SHA512_digest__doc__,
+"Return the digest value as a string of binary data.");
+
+static PyObject *
+SHA512_digest(SHAobject *self, PyObject *unused)
+{
+    unsigned char digest[SHA_DIGESTSIZE];
+    SHAobject temp;
+
+    SHAcopy(self, &temp);
+    sha512_final(digest, &temp);
+    return PyString_FromStringAndSize((const char *)digest, self->digestsize);
+}
+
+PyDoc_STRVAR(SHA512_hexdigest__doc__,
+"Return the digest value as a string of hexadecimal digits.");
+
+static PyObject *
+SHA512_hexdigest(SHAobject *self, PyObject *unused)
+{
+    unsigned char digest[SHA_DIGESTSIZE];
+    SHAobject temp;
+    PyObject *retval;
+    char *hex_digest;
+    int i, j;
+
+    /* Get the raw (binary) digest value */
+    SHAcopy(self, &temp);
+    sha512_final(digest, &temp);
+
+    /* Create a new string */
+    retval = PyString_FromStringAndSize(NULL, self->digestsize * 2);
+    if (!retval)
+	    return NULL;
+    hex_digest = PyString_AsString(retval);
+    if (!hex_digest) {
+	    Py_DECREF(retval);
+	    return NULL;
+    }
+
+    /* Make hex version of the digest */
+    for (i=j=0; i<self->digestsize; i++) {
+        char c;
+        c = (digest[i] >> 4) & 0xf;
+	c = (c>9) ? c+'a'-10 : c + '0';
+        hex_digest[j++] = c;
+        c = (digest[i] & 0xf);
+	c = (c>9) ? c+'a'-10 : c + '0';
+        hex_digest[j++] = c;
+    }
+    return retval;
+}
+
+PyDoc_STRVAR(SHA512_update__doc__,
+"Update this hash object's state with the provided string.");
+
+static PyObject *
+SHA512_update(SHAobject *self, PyObject *args)
+{
+    unsigned char *cp;
+    int len;
+
+    if (!PyArg_ParseTuple(args, "s#:update", &cp, &len))
+        return NULL;
+
+    sha512_update(self, cp, len);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyMethodDef SHA_methods[] = {
+    {"copy",	  (PyCFunction)SHA512_copy,      METH_NOARGS, SHA512_copy__doc__},
+    {"digest",	  (PyCFunction)SHA512_digest,    METH_NOARGS, SHA512_digest__doc__},
+    {"hexdigest", (PyCFunction)SHA512_hexdigest, METH_NOARGS, SHA512_hexdigest__doc__},
+    {"update",	  (PyCFunction)SHA512_update,    METH_VARARGS, SHA512_update__doc__},
+    {NULL,	  NULL}		/* sentinel */
+};
+
+static PyObject *
+SHA512_get_block_size(PyObject *self, void *closure)
+{
+    return PyInt_FromLong(SHA_BLOCKSIZE);
+}
+
+static PyObject *
+SHA512_get_name(PyObject *self, void *closure)
+{
+    if (((SHAobject *)self)->digestsize == 64)
+        return PyString_FromStringAndSize("SHA512", 6);
+    else
+        return PyString_FromStringAndSize("SHA384", 6);
+}
+
+static PyGetSetDef SHA_getseters[] = {
+    {"block_size",
+     (getter)SHA512_get_block_size, NULL,
+     NULL,
+     NULL},
+    {"name",
+     (getter)SHA512_get_name, NULL,
+     NULL,
+     NULL},
+    {NULL}  /* Sentinel */
+};
+
+static PyMemberDef SHA_members[] = {
+    {"digest_size", T_INT, offsetof(SHAobject, digestsize), READONLY, NULL},
+    /* the old md5 and sha modules support 'digest_size' as in PEP 247.
+     * the old sha module also supported 'digestsize'.  ugh. */
+    {"digestsize", T_INT, offsetof(SHAobject, digestsize), READONLY, NULL},
+    {NULL}  /* Sentinel */
+};
+
+static PyTypeObject SHA384type = {
+    PyObject_HEAD_INIT(NULL)
+    0,			/*ob_size*/
+    "_sha512.sha384",	/*tp_name*/
+    sizeof(SHAobject),	/*tp_size*/
+    0,			/*tp_itemsize*/
+    /* methods */
+    SHA512_dealloc,	/*tp_dealloc*/
+    0,			/*tp_print*/
+    0,          	/*tp_getattr*/
+    0,                  /*tp_setattr*/
+    0,                  /*tp_compare*/
+    0,                  /*tp_repr*/
+    0,                  /*tp_as_number*/
+    0,                  /*tp_as_sequence*/
+    0,                  /*tp_as_mapping*/
+    0,                  /*tp_hash*/
+    0,                  /*tp_call*/
+    0,                  /*tp_str*/
+    0,                  /*tp_getattro*/
+    0,                  /*tp_setattro*/
+    0,                  /*tp_as_buffer*/
+    Py_TPFLAGS_DEFAULT, /*tp_flags*/
+    0,                  /*tp_doc*/
+    0,                  /*tp_traverse*/
+    0,			/*tp_clear*/
+    0,			/*tp_richcompare*/
+    0,			/*tp_weaklistoffset*/
+    0,			/*tp_iter*/
+    0,			/*tp_iternext*/
+    SHA_methods,	/* tp_methods */
+    SHA_members,	/* tp_members */
+    SHA_getseters,      /* tp_getset */
+};
+
+static PyTypeObject SHA512type = {
+    PyObject_HEAD_INIT(NULL)
+    0,			/*ob_size*/
+    "_sha512.sha512",	/*tp_name*/
+    sizeof(SHAobject),	/*tp_size*/
+    0,			/*tp_itemsize*/
+    /* methods */
+    SHA512_dealloc,	/*tp_dealloc*/
+    0,			/*tp_print*/
+    0,          	/*tp_getattr*/
+    0,                  /*tp_setattr*/
+    0,                  /*tp_compare*/
+    0,                  /*tp_repr*/
+    0,                  /*tp_as_number*/
+    0,                  /*tp_as_sequence*/
+    0,                  /*tp_as_mapping*/
+    0,                  /*tp_hash*/
+    0,                  /*tp_call*/
+    0,                  /*tp_str*/
+    0,                  /*tp_getattro*/
+    0,                  /*tp_setattro*/
+    0,                  /*tp_as_buffer*/
+    Py_TPFLAGS_DEFAULT, /*tp_flags*/
+    0,                  /*tp_doc*/
+    0,                  /*tp_traverse*/
+    0,			/*tp_clear*/
+    0,			/*tp_richcompare*/
+    0,			/*tp_weaklistoffset*/
+    0,			/*tp_iter*/
+    0,			/*tp_iternext*/
+    SHA_methods,	/* tp_methods */
+    SHA_members,	/* tp_members */
+    SHA_getseters,      /* tp_getset */
+};
+
+
+/* The single module-level function: new() */
+
+PyDoc_STRVAR(SHA512_new__doc__,
+"Return a new SHA-512 hash object; optionally initialized with a string.");
+
+static PyObject *
+SHA512_new(PyObject *self, PyObject *args, PyObject *kwdict)
+{
+    static char *kwlist[] = {"string", NULL};
+    SHAobject *new;
+    unsigned char *cp = NULL;
+    int len;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|s#:new", kwlist,
+                                     &cp, &len)) {
+        return NULL;
+    }
+
+    if ((new = newSHA512object()) == NULL)
+        return NULL;
+
+    sha512_init(new);
+
+    if (PyErr_Occurred()) {
+        Py_DECREF(new);
+        return NULL;
+    }
+    if (cp)
+        sha512_update(new, cp, len);
+
+    return (PyObject *)new;
+}
+
+PyDoc_STRVAR(SHA384_new__doc__,
+"Return a new SHA-384 hash object; optionally initialized with a string.");
+
+static PyObject *
+SHA384_new(PyObject *self, PyObject *args, PyObject *kwdict)
+{
+    static char *kwlist[] = {"string", NULL};
+    SHAobject *new;
+    unsigned char *cp = NULL;
+    int len;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|s#:new", kwlist,
+                                     &cp, &len)) {
+        return NULL;
+    }
+
+    if ((new = newSHA384object()) == NULL)
+        return NULL;
+
+    sha384_init(new);
+
+    if (PyErr_Occurred()) {
+        Py_DECREF(new);
+        return NULL;
+    }
+    if (cp)
+        sha512_update(new, cp, len);
+
+    return (PyObject *)new;
+}
+
+
+/* List of functions exported by this module */
+
+static struct PyMethodDef SHA_functions[] = {
+    {"sha512", (PyCFunction)SHA512_new, METH_VARARGS|METH_KEYWORDS, SHA512_new__doc__},
+    {"sha384", (PyCFunction)SHA384_new, METH_VARARGS|METH_KEYWORDS, SHA384_new__doc__},
+    {NULL,	NULL}		 /* Sentinel */
+};
+
+
+/* Initialize this module. */
+
+#define insint(n,v) { PyModule_AddIntConstant(m,n,v); }
+
+PyMODINIT_FUNC
+init_sha512(void)
+{
+    PyObject *m;
+
+    SHA384type.ob_type = &PyType_Type;
+    if (PyType_Ready(&SHA384type) < 0)
+        return;
+    SHA512type.ob_type = &PyType_Type;
+    if (PyType_Ready(&SHA512type) < 0)
+        return;
+    m = Py_InitModule("_sha512", SHA_functions);
+    if (m == NULL)
+	return;
+}
+
+#endif
diff --git a/src/Modules/shamodule.c b/src/Modules/shamodule.c
new file mode 100644
--- /dev/null
+++ b/src/Modules/shamodule.c
@@ -0,0 +1,593 @@
+/* SHA module */
+
+/* This module provides an interface to NIST's Secure Hash Algorithm */
+
+/* See below for information about the original code this module was
+   based upon. Additional work performed by:
+
+   Andrew Kuchling (amk at amk.ca)
+   Greg Stein (gstein at lyra.org)
+
+   Copyright (C) 2005   Gregory P. Smith (greg at krypto.org)
+   Licensed to PSF under a Contributor Agreement.
+
+*/
+
+/* SHA objects */
+
+#include "Python.h"
+#include "structmember.h"
+
+
+/* Endianness testing and definitions */
+#define TestEndianness(variable) {int i=1; variable=PCT_BIG_ENDIAN;\
+	if (*((char*)&i)==1) variable=PCT_LITTLE_ENDIAN;}
+
+#define PCT_LITTLE_ENDIAN 1
+#define PCT_BIG_ENDIAN 0
+
+/* Some useful types */
+
+typedef unsigned char SHA_BYTE;
+
+#if SIZEOF_INT == 4
+typedef unsigned int SHA_INT32;	/* 32-bit integer */
+#else
+/* not defined. compilation will die. */
+#endif
+
+/* The SHA block size and message digest sizes, in bytes */
+
+#define SHA_BLOCKSIZE    64
+#define SHA_DIGESTSIZE  20
+
+/* The structure for storing SHS info */
+
+typedef struct {
+    PyObject_HEAD
+    SHA_INT32 digest[5];		/* Message digest */
+    SHA_INT32 count_lo, count_hi;	/* 64-bit bit count */
+    SHA_BYTE data[SHA_BLOCKSIZE];	/* SHA data buffer */
+    int Endianness;
+    int local;				/* unprocessed amount in data */
+} SHAobject;
+
+/* When run on a little-endian CPU we need to perform byte reversal on an
+   array of longwords. */
+
+static void longReverse(SHA_INT32 *buffer, int byteCount, int Endianness)
+{
+    SHA_INT32 value;
+
+    if ( Endianness == PCT_BIG_ENDIAN )
+	return;
+
+    byteCount /= sizeof(*buffer);
+    while (byteCount--) {
+        value = *buffer;
+        value = ( ( value & 0xFF00FF00L ) >> 8  ) | \
+                ( ( value & 0x00FF00FFL ) << 8 );
+        *buffer++ = ( value << 16 ) | ( value >> 16 );
+    }
+}
+
+static void SHAcopy(SHAobject *src, SHAobject *dest)
+{
+    dest->Endianness = src->Endianness;
+    dest->local = src->local;
+    dest->count_lo = src->count_lo;
+    dest->count_hi = src->count_hi;
+    memcpy(dest->digest, src->digest, sizeof(src->digest));
+    memcpy(dest->data, src->data, sizeof(src->data));
+}
+
+
+/* ------------------------------------------------------------------------
+ *
+ * This code for the SHA algorithm was noted as public domain. The original
+ * headers are pasted below.
+ *
+ * Several changes have been made to make it more compatible with the
+ * Python environment and desired interface.
+ *
+ */
+
+/* NIST Secure Hash Algorithm */
+/* heavily modified by Uwe Hollerbach <uh at alumni.caltech edu> */
+/* from Peter C. Gutmann's implementation as found in */
+/* Applied Cryptography by Bruce Schneier */
+/* Further modifications to include the "UNRAVEL" stuff, below */
+
+/* This code is in the public domain */
+
+/* UNRAVEL should be fastest & biggest */
+/* UNROLL_LOOPS should be just as big, but slightly slower */
+/* both undefined should be smallest and slowest */
+
+#define UNRAVEL
+/* #define UNROLL_LOOPS */
+
+/* The SHA f()-functions.  The f1 and f3 functions can be optimized to
+   save one boolean operation each - thanks to Rich Schroeppel,
+   rcs at cs.arizona.edu for discovering this */
+
+/*#define f1(x,y,z)	((x & y) | (~x & z))		// Rounds  0-19 */
+#define f1(x,y,z)	(z ^ (x & (y ^ z)))		/* Rounds  0-19 */
+#define f2(x,y,z)	(x ^ y ^ z)			/* Rounds 20-39 */
+/*#define f3(x,y,z)	((x & y) | (x & z) | (y & z))	// Rounds 40-59 */
+#define f3(x,y,z)	((x & y) | (z & (x | y)))	/* Rounds 40-59 */
+#define f4(x,y,z)	(x ^ y ^ z)			/* Rounds 60-79 */
+
+/* SHA constants */
+
+#define CONST1		0x5a827999L			/* Rounds  0-19 */
+#define CONST2		0x6ed9eba1L			/* Rounds 20-39 */
+#define CONST3		0x8f1bbcdcL			/* Rounds 40-59 */
+#define CONST4		0xca62c1d6L			/* Rounds 60-79 */
+
+/* 32-bit rotate */
+
+#define R32(x,n)	((x << n) | (x >> (32 - n)))
+
+/* the generic case, for when the overall rotation is not unraveled */
+
+#define FG(n)	\
+    T = R32(A,5) + f##n(B,C,D) + E + *WP++ + CONST##n;	\
+    E = D; D = C; C = R32(B,30); B = A; A = T
+
+/* specific cases, for when the overall rotation is unraveled */
+
+#define FA(n)	\
+    T = R32(A,5) + f##n(B,C,D) + E + *WP++ + CONST##n; B = R32(B,30)
+
+#define FB(n)	\
+    E = R32(T,5) + f##n(A,B,C) + D + *WP++ + CONST##n; A = R32(A,30)
+
+#define FC(n)	\
+    D = R32(E,5) + f##n(T,A,B) + C + *WP++ + CONST##n; T = R32(T,30)
+
+#define FD(n)	\
+    C = R32(D,5) + f##n(E,T,A) + B + *WP++ + CONST##n; E = R32(E,30)
+
+#define FE(n)	\
+    B = R32(C,5) + f##n(D,E,T) + A + *WP++ + CONST##n; D = R32(D,30)
+
+#define FT(n)	\
+    A = R32(B,5) + f##n(C,D,E) + T + *WP++ + CONST##n; C = R32(C,30)
+
+/* do SHA transformation */
+
+static void
+sha_transform(SHAobject *sha_info)
+{
+    int i;
+    SHA_INT32 T, A, B, C, D, E, W[80], *WP;
+
+    memcpy(W, sha_info->data, sizeof(sha_info->data));
+    longReverse(W, (int)sizeof(sha_info->data), sha_info->Endianness);
+
+    for (i = 16; i < 80; ++i) {
+	W[i] = W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16];
+
+	/* extra rotation fix */
+	W[i] = R32(W[i], 1);
+    }
+    A = sha_info->digest[0];
+    B = sha_info->digest[1];
+    C = sha_info->digest[2];
+    D = sha_info->digest[3];
+    E = sha_info->digest[4];
+    WP = W;
+#ifdef UNRAVEL
+    FA(1); FB(1); FC(1); FD(1); FE(1); FT(1); FA(1); FB(1); FC(1); FD(1);
+    FE(1); FT(1); FA(1); FB(1); FC(1); FD(1); FE(1); FT(1); FA(1); FB(1);
+    FC(2); FD(2); FE(2); FT(2); FA(2); FB(2); FC(2); FD(2); FE(2); FT(2);
+    FA(2); FB(2); FC(2); FD(2); FE(2); FT(2); FA(2); FB(2); FC(2); FD(2);
+    FE(3); FT(3); FA(3); FB(3); FC(3); FD(3); FE(3); FT(3); FA(3); FB(3);
+    FC(3); FD(3); FE(3); FT(3); FA(3); FB(3); FC(3); FD(3); FE(3); FT(3);
+    FA(4); FB(4); FC(4); FD(4); FE(4); FT(4); FA(4); FB(4); FC(4); FD(4);
+    FE(4); FT(4); FA(4); FB(4); FC(4); FD(4); FE(4); FT(4); FA(4); FB(4);
+    sha_info->digest[0] += E;
+    sha_info->digest[1] += T;
+    sha_info->digest[2] += A;
+    sha_info->digest[3] += B;
+    sha_info->digest[4] += C;
+#else /* !UNRAVEL */
+#ifdef UNROLL_LOOPS
+    FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1);
+    FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1);
+    FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2);
+    FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2);
+    FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3);
+    FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3);
+    FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4);
+    FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4);
+#else /* !UNROLL_LOOPS */
+    for (i =  0; i < 20; ++i) { FG(1); }
+    for (i = 20; i < 40; ++i) { FG(2); }
+    for (i = 40; i < 60; ++i) { FG(3); }
+    for (i = 60; i < 80; ++i) { FG(4); }
+#endif /* !UNROLL_LOOPS */
+    sha_info->digest[0] += A;
+    sha_info->digest[1] += B;
+    sha_info->digest[2] += C;
+    sha_info->digest[3] += D;
+    sha_info->digest[4] += E;
+#endif /* !UNRAVEL */
+}
+
+/* initialize the SHA digest */
+
+static void
+sha_init(SHAobject *sha_info)
+{
+    TestEndianness(sha_info->Endianness)
+
+    sha_info->digest[0] = 0x67452301L;
+    sha_info->digest[1] = 0xefcdab89L;
+    sha_info->digest[2] = 0x98badcfeL;
+    sha_info->digest[3] = 0x10325476L;
+    sha_info->digest[4] = 0xc3d2e1f0L;
+    sha_info->count_lo = 0L;
+    sha_info->count_hi = 0L;
+    sha_info->local = 0;
+}
+
+/* update the SHA digest */
+
+static void
+sha_update(SHAobject *sha_info, SHA_BYTE *buffer, int count)
+{
+    int i;
+    SHA_INT32 clo;
+
+    clo = sha_info->count_lo + ((SHA_INT32) count << 3);
+    if (clo < sha_info->count_lo) {
+        ++sha_info->count_hi;
+    }
+    sha_info->count_lo = clo;
+    sha_info->count_hi += (SHA_INT32) count >> 29;
+    if (sha_info->local) {
+        i = SHA_BLOCKSIZE - sha_info->local;
+        if (i > count) {
+            i = count;
+        }
+        memcpy(((SHA_BYTE *) sha_info->data) + sha_info->local, buffer, i);
+        count -= i;
+        buffer += i;
+        sha_info->local += i;
+        if (sha_info->local == SHA_BLOCKSIZE) {
+            sha_transform(sha_info);
+        }
+        else {
+            return;
+        }
+    }
+    while (count >= SHA_BLOCKSIZE) {
+        memcpy(sha_info->data, buffer, SHA_BLOCKSIZE);
+        buffer += SHA_BLOCKSIZE;
+        count -= SHA_BLOCKSIZE;
+        sha_transform(sha_info);
+    }
+    memcpy(sha_info->data, buffer, count);
+    sha_info->local = count;
+}
+
+/* finish computing the SHA digest */
+
+static void
+sha_final(unsigned char digest[20], SHAobject *sha_info)
+{
+    int count;
+    SHA_INT32 lo_bit_count, hi_bit_count;
+
+    lo_bit_count = sha_info->count_lo;
+    hi_bit_count = sha_info->count_hi;
+    count = (int) ((lo_bit_count >> 3) & 0x3f);
+    ((SHA_BYTE *) sha_info->data)[count++] = 0x80;
+    if (count > SHA_BLOCKSIZE - 8) {
+	memset(((SHA_BYTE *) sha_info->data) + count, 0,
+	       SHA_BLOCKSIZE - count);
+	sha_transform(sha_info);
+	memset((SHA_BYTE *) sha_info->data, 0, SHA_BLOCKSIZE - 8);
+    }
+    else {
+	memset(((SHA_BYTE *) sha_info->data) + count, 0,
+	       SHA_BLOCKSIZE - 8 - count);
+    }
+
+    /* GJS: note that we add the hi/lo in big-endian. sha_transform will
+       swap these values into host-order. */
+    sha_info->data[56] = (hi_bit_count >> 24) & 0xff;
+    sha_info->data[57] = (hi_bit_count >> 16) & 0xff;
+    sha_info->data[58] = (hi_bit_count >>  8) & 0xff;
+    sha_info->data[59] = (hi_bit_count >>  0) & 0xff;
+    sha_info->data[60] = (lo_bit_count >> 24) & 0xff;
+    sha_info->data[61] = (lo_bit_count >> 16) & 0xff;
+    sha_info->data[62] = (lo_bit_count >>  8) & 0xff;
+    sha_info->data[63] = (lo_bit_count >>  0) & 0xff;
+    sha_transform(sha_info);
+    digest[ 0] = (unsigned char) ((sha_info->digest[0] >> 24) & 0xff);
+    digest[ 1] = (unsigned char) ((sha_info->digest[0] >> 16) & 0xff);
+    digest[ 2] = (unsigned char) ((sha_info->digest[0] >>  8) & 0xff);
+    digest[ 3] = (unsigned char) ((sha_info->digest[0]      ) & 0xff);
+    digest[ 4] = (unsigned char) ((sha_info->digest[1] >> 24) & 0xff);
+    digest[ 5] = (unsigned char) ((sha_info->digest[1] >> 16) & 0xff);
+    digest[ 6] = (unsigned char) ((sha_info->digest[1] >>  8) & 0xff);
+    digest[ 7] = (unsigned char) ((sha_info->digest[1]      ) & 0xff);
+    digest[ 8] = (unsigned char) ((sha_info->digest[2] >> 24) & 0xff);
+    digest[ 9] = (unsigned char) ((sha_info->digest[2] >> 16) & 0xff);
+    digest[10] = (unsigned char) ((sha_info->digest[2] >>  8) & 0xff);
+    digest[11] = (unsigned char) ((sha_info->digest[2]      ) & 0xff);
+    digest[12] = (unsigned char) ((sha_info->digest[3] >> 24) & 0xff);
+    digest[13] = (unsigned char) ((sha_info->digest[3] >> 16) & 0xff);
+    digest[14] = (unsigned char) ((sha_info->digest[3] >>  8) & 0xff);
+    digest[15] = (unsigned char) ((sha_info->digest[3]      ) & 0xff);
+    digest[16] = (unsigned char) ((sha_info->digest[4] >> 24) & 0xff);
+    digest[17] = (unsigned char) ((sha_info->digest[4] >> 16) & 0xff);
+    digest[18] = (unsigned char) ((sha_info->digest[4] >>  8) & 0xff);
+    digest[19] = (unsigned char) ((sha_info->digest[4]      ) & 0xff);
+}
+
+/*
+ * End of copied SHA code.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+static PyTypeObject SHAtype;
+
+
+static SHAobject *
+newSHAobject(void)
+{
+    return (SHAobject *)PyObject_New(SHAobject, &SHAtype);
+}
+
+/* Internal methods for a hashing object */
+
+static void
+SHA_dealloc(PyObject *ptr)
+{
+    PyObject_Del(ptr);
+}
+
+
+/* External methods for a hashing object */
+
+PyDoc_STRVAR(SHA_copy__doc__, "Return a copy of the hashing object.");
+
+static PyObject *
+SHA_copy(SHAobject *self, PyObject *unused)
+{
+    SHAobject *newobj;
+
+    if ( (newobj = newSHAobject())==NULL)
+        return NULL;
+
+    SHAcopy(self, newobj);
+    return (PyObject *)newobj;
+}
+
+PyDoc_STRVAR(SHA_digest__doc__,
+"Return the digest value as a string of binary data.");
+
+static PyObject *
+SHA_digest(SHAobject *self, PyObject *unused)
+{
+    unsigned char digest[SHA_DIGESTSIZE];
+    SHAobject temp;
+
+    SHAcopy(self, &temp);
+    sha_final(digest, &temp);
+    return PyString_FromStringAndSize((const char *)digest, sizeof(digest));
+}
+
+PyDoc_STRVAR(SHA_hexdigest__doc__,
+"Return the digest value as a string of hexadecimal digits.");
+
+static PyObject *
+SHA_hexdigest(SHAobject *self, PyObject *unused)
+{
+    unsigned char digest[SHA_DIGESTSIZE];
+    SHAobject temp;
+    PyObject *retval;
+    char *hex_digest;
+    int i, j;
+
+    /* Get the raw (binary) digest value */
+    SHAcopy(self, &temp);
+    sha_final(digest, &temp);
+
+    /* Create a new string */
+    retval = PyString_FromStringAndSize(NULL, sizeof(digest) * 2);
+    if (!retval)
+	    return NULL;
+    hex_digest = PyString_AsString(retval);
+    if (!hex_digest) {
+	    Py_DECREF(retval);
+	    return NULL;
+    }
+
+    /* Make hex version of the digest */
+    for(i=j=0; i<sizeof(digest); i++) {
+        char c;
+        c = (digest[i] >> 4) & 0xf;
+	c = (c>9) ? c+'a'-10 : c + '0';
+        hex_digest[j++] = c;
+        c = (digest[i] & 0xf);
+	c = (c>9) ? c+'a'-10 : c + '0';
+        hex_digest[j++] = c;
+    }
+    return retval;
+}
+
+PyDoc_STRVAR(SHA_update__doc__,
+"Update this hashing object's state with the provided string.");
+
+static PyObject *
+SHA_update(SHAobject *self, PyObject *args)
+{
+    unsigned char *cp;
+    int len;
+
+    if (!PyArg_ParseTuple(args, "s#:update", &cp, &len))
+        return NULL;
+
+    sha_update(self, cp, len);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyMethodDef SHA_methods[] = {
+    {"copy",	  (PyCFunction)SHA_copy,      METH_NOARGS,  SHA_copy__doc__},
+    {"digest",	  (PyCFunction)SHA_digest,    METH_NOARGS,  SHA_digest__doc__},
+    {"hexdigest", (PyCFunction)SHA_hexdigest, METH_NOARGS,  SHA_hexdigest__doc__},
+    {"update",	  (PyCFunction)SHA_update,    METH_VARARGS, SHA_update__doc__},
+    {NULL,	  NULL}		/* sentinel */
+};
+
+static PyObject *
+SHA_get_block_size(PyObject *self, void *closure)
+{
+    return PyInt_FromLong(SHA_BLOCKSIZE);
+}
+
+static PyObject *
+SHA_get_digest_size(PyObject *self, void *closure)
+{
+    return PyInt_FromLong(SHA_DIGESTSIZE);
+}
+
+static PyObject *
+SHA_get_name(PyObject *self, void *closure)
+{
+    return PyString_FromStringAndSize("SHA1", 4);
+}
+
+static PyGetSetDef SHA_getseters[] = {
+    {"digest_size",
+     (getter)SHA_get_digest_size, NULL,
+     NULL,
+     NULL},
+    {"block_size",
+     (getter)SHA_get_block_size, NULL,
+     NULL,
+     NULL},
+    {"name",
+     (getter)SHA_get_name, NULL,
+     NULL,
+     NULL},
+    /* the old md5 and sha modules support 'digest_size' as in PEP 247.
+     * the old sha module also supported 'digestsize'.  ugh. */
+    {"digestsize",
+     (getter)SHA_get_digest_size, NULL,
+     NULL,
+     NULL},
+    {NULL}  /* Sentinel */
+};
+
+static PyTypeObject SHAtype = {
+    PyObject_HEAD_INIT(NULL)
+    0,			/*ob_size*/
+    "_sha.sha",		/*tp_name*/
+    sizeof(SHAobject),	/*tp_size*/
+    0,			/*tp_itemsize*/
+    /* methods */
+    SHA_dealloc,	/*tp_dealloc*/
+    0,			/*tp_print*/
+    0,                  /*tp_getattr*/
+    0,                  /*tp_setattr*/
+    0,                  /*tp_compare*/
+    0,                  /*tp_repr*/
+    0,                  /*tp_as_number*/
+    0,                  /*tp_as_sequence*/
+    0,                  /*tp_as_mapping*/
+    0,                  /*tp_hash*/
+    0,                  /*tp_call*/
+    0,                  /*tp_str*/
+    0,                  /*tp_getattro*/
+    0,                  /*tp_setattro*/
+    0,                  /*tp_as_buffer*/
+    Py_TPFLAGS_DEFAULT, /*tp_flags*/
+    0,                  /*tp_doc*/
+    0,                  /*tp_traverse*/
+    0,			/*tp_clear*/
+    0,			/*tp_richcompare*/
+    0,			/*tp_weaklistoffset*/
+    0,			/*tp_iter*/
+    0,			/*tp_iternext*/
+    SHA_methods,	/* tp_methods */
+    0,                  /* tp_members */
+    SHA_getseters,      /* tp_getset */
+};
+
+
+/* The single module-level function: new() */
+
+PyDoc_STRVAR(SHA_new__doc__,
+"Return a new SHA hashing object.  An optional string argument\n\
+may be provided; if present, this string will be automatically\n\
+hashed.");
+
+static PyObject *
+SHA_new(PyObject *self, PyObject *args, PyObject *kwdict)
+{
+    static char *kwlist[] = {"string", NULL};
+    SHAobject *new;
+    unsigned char *cp = NULL;
+    int len;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|s#:new", kwlist,
+                                     &cp, &len)) {
+        return NULL;
+    }
+
+    if ((new = newSHAobject()) == NULL)
+        return NULL;
+
+    sha_init(new);
+
+    if (PyErr_Occurred()) {
+        Py_DECREF(new);
+        return NULL;
+    }
+    if (cp)
+        sha_update(new, cp, len);
+
+    return (PyObject *)new;
+}
+
+
+/* List of functions exported by this module */
+
+static struct PyMethodDef SHA_functions[] = {
+    {"new", (PyCFunction)SHA_new, METH_VARARGS|METH_KEYWORDS, SHA_new__doc__},
+    {NULL,	NULL}		 /* Sentinel */
+};
+
+
+/* Initialize this module. */
+
+#define insint(n,v) { PyModule_AddIntConstant(m,n,v); }
+
+PyMODINIT_FUNC
+init_sha(void)
+{
+    PyObject *m;
+
+    SHAtype.ob_type = &PyType_Type;
+    if (PyType_Ready(&SHAtype) < 0)
+        return;
+    m = Py_InitModule("_sha", SHA_functions);
+    if (m == NULL)
+	return;
+
+    /* Add some symbolic constants to the module */
+    insint("blocksize", 1);  /* For future use, in case some hash
+                                functions require an integral number of
+                                blocks */ 
+    insint("digestsize", 20);
+    insint("digest_size", 20);
+}
diff --git a/src/README.txt b/src/README.txt
--- a/src/README.txt
+++ b/src/README.txt
@@ -2,7 +2,7 @@
 Distutils2
 ==========
 
-Welcome to Distutils2 !
+Welcome to Distutils2!
 
 Distutils2 is the new version of Distutils. It's not backward compatible with
 Distutils but provides more features, and implement most new packaging
@@ -10,6 +10,6 @@
 
 See the documentation at http://packages.python.org/Distutils2 for more info.
 
-**Beware that Distutils2 is its in early stage and should not be used in
+**Beware that Distutils2 is in its early stage and should not be used in
 production. Its API is subject to changes**
 
diff --git a/src/check.sh b/src/check.sh
new file mode 100755
--- /dev/null
+++ b/src/check.sh
@@ -0,0 +1,2 @@
+pep8 distutils2
+pyflakes distutils2
diff --git a/src/distutils2/README b/src/distutils2/README
--- a/src/distutils2/README
+++ b/src/distutils2/README
@@ -1,4 +1,4 @@
-This directory contains the Distutils package.
+This directory contains the Distutils2 package.
 
 There's a full documentation available at:
 
@@ -8,6 +8,6 @@
 
     http://www.python.org/sigs/distutils-sig/
 
-WARNING: Distutils2 must remain compatible with 2.4
+WARNING: Distutils2 must remain compatible with Python 2.4
 
 $Id: README 70017 2009-02-27 12:53:34Z tarek.ziade $
diff --git a/src/distutils2/__init__.py b/src/distutils2/__init__.py
--- a/src/distutils2/__init__.py
+++ b/src/distutils2/__init__.py
@@ -1,16 +1,26 @@
 """distutils
 
-The main package for the Python Module Distribution Utilities.  Normally
-used from a setup script as
+The main package for the Python Distribution Utilities 2.  Setup
+scripts should import the setup function from distutils2.core:
 
-   from distutils.core import setup
+    from distutils2.core import setup
 
-   setup (...)
+    setup(name=..., version=..., ...)
+
+Third-party tools can use parts of Distutils2 as building blocks
+without causing the other modules to be imported:
+
+    import distutils2.version
+    import distutils2.pypi.simple
+    import distutils2.tests.pypi_server
 """
-__all__ = ['__version__', 'setup']
+__all__ = ['__version__']
 
 __revision__ = "$Id: __init__.py 78020 2010-02-06 16:37:32Z benjamin.peterson $"
 __version__ = "1.0a2"
 
-from distutils2.core import setup
 
+# when set to True, converts doctests by default too 
+run_2to3_on_doctests = True
+# Standard package names for fixer packages
+lib2to3_fixer_packages = ['lib2to3.fixes']
diff --git a/src/distutils2/_backport/hashlib.py b/src/distutils2/_backport/hashlib.py
new file mode 100644
--- /dev/null
+++ b/src/distutils2/_backport/hashlib.py
@@ -0,0 +1,143 @@
+# $Id$
+#
+#  Copyright (C) 2005   Gregory P. Smith (greg at krypto.org)
+#  Licensed to PSF under a Contributor Agreement.
+#
+
+__doc__ = """hashlib module - A common interface to many hash functions.
+
+new(name, string='') - returns a new hash object implementing the
+                       given hash function; initializing the hash
+                       using the given string data.
+
+Named constructor functions are also available, these are much faster
+than using new():
+
+md5(), sha1(), sha224(), sha256(), sha384(), and sha512()
+
+More algorithms may be available on your platform but the above are
+guaranteed to exist.
+
+NOTE: If you want the adler32 or crc32 hash functions they are available in
+the zlib module.
+
+Choose your hash function wisely.  Some have known collision weaknesses.
+sha384 and sha512 will be slow on 32 bit platforms.
+
+Hash objects have these methods:
+ - update(arg): Update the hash object with the string arg. Repeated calls
+                are equivalent to a single call with the concatenation of all
+                the arguments.
+ - digest():    Return the digest of the strings passed to the update() method
+                so far. This may contain non-ASCII characters, including
+                NUL bytes.
+ - hexdigest(): Like digest() except the digest is returned as a string of
+                double length, containing only hexadecimal digits.
+ - copy():      Return a copy (clone) of the hash object. This can be used to
+                efficiently compute the digests of strings that share a common
+                initial substring.
+
+For example, to obtain the digest of the string 'Nobody inspects the
+spammish repetition':
+
+    >>> import hashlib
+    >>> m = hashlib.md5()
+    >>> m.update("Nobody inspects")
+    >>> m.update(" the spammish repetition")
+    >>> m.digest()
+    '\\xbbd\\x9c\\x83\\xdd\\x1e\\xa5\\xc9\\xd9\\xde\\xc9\\xa1\\x8d\\xf0\\xff\\xe9'
+
+More condensed:
+
+    >>> hashlib.sha224("Nobody inspects the spammish repetition").hexdigest()
+    'a4337bc45a8fc544c03f52dc550cd6e1e87021bc896588bd79e901e2'
+
+"""
+
+# This tuple and __get_builtin_constructor() must be modified if a new
+# always available algorithm is added.
+__always_supported = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512')
+
+algorithms = __always_supported
+
+__all__ = __always_supported + ('new', 'algorithms')
+
+
+def __get_builtin_constructor(name):
+    if name in ('SHA1', 'sha1'):
+        import _sha
+        return _sha.new
+    elif name in ('MD5', 'md5'):
+        import _md5
+        return _md5.new
+    elif name in ('SHA256', 'sha256', 'SHA224', 'sha224'):
+        import _sha256
+        bs = name[3:]
+        if bs == '256':
+            return _sha256.sha256
+        elif bs == '224':
+            return _sha256.sha224
+    elif name in ('SHA512', 'sha512', 'SHA384', 'sha384'):
+        import _sha512
+        bs = name[3:]
+        if bs == '512':
+            return _sha512.sha512
+        elif bs == '384':
+            return _sha512.sha384
+
+    raise ValueError('unsupported hash type %s' % name)
+
+
+def __get_openssl_constructor(name):
+    try:
+        f = getattr(_hashlib, 'openssl_' + name)
+        # Allow the C module to raise ValueError.  The function will be
+        # defined but the hash not actually available thanks to OpenSSL.
+        f()
+        # Use the C function directly (very fast)
+        return f
+    except (AttributeError, ValueError):
+        return __get_builtin_constructor(name)
+
+
+def __py_new(name, string=''):
+    """new(name, string='') - Return a new hashing object using the named algorithm;
+    optionally initialized with a string.
+    """
+    return __get_builtin_constructor(name)(string)
+
+
+def __hash_new(name, string=''):
+    """new(name, string='') - Return a new hashing object using the named algorithm;
+    optionally initialized with a string.
+    """
+    try:
+        return _hashlib.new(name, string)
+    except ValueError:
+        # If the _hashlib module (OpenSSL) doesn't support the named
+        # hash, try using our builtin implementations.
+        # This allows for SHA224/256 and SHA384/512 support even though
+        # the OpenSSL library prior to 0.9.8 doesn't provide them.
+        return __get_builtin_constructor(name)(string)
+
+
+try:
+    import _hashlib
+    new = __hash_new
+    __get_hash = __get_openssl_constructor
+except ImportError:
+    new = __py_new
+    __get_hash = __get_builtin_constructor
+
+for __func_name in __always_supported:
+    # try them all, some may not work due to the OpenSSL
+    # version not supporting that algorithm.
+    try:
+        globals()[__func_name] = __get_hash(__func_name)
+    except ValueError:
+        import logging
+        logging.exception('code for hash %s was not found.', __func_name)
+
+# Cleanup locals()
+del __always_supported, __func_name, __get_hash
+del __py_new, __hash_new, __get_openssl_constructor
diff --git a/src/distutils2/_backport/pkgutil.py b/src/distutils2/_backport/pkgutil.py
--- a/src/distutils2/_backport/pkgutil.py
+++ b/src/distutils2/_backport/pkgutil.py
@@ -12,10 +12,17 @@
 from distutils2.errors import DistutilsError
 from distutils2.metadata import DistributionMetadata
 from distutils2.version import suggest_normalized_version, VersionPredicate
+import zipimport
+try:
+    import cStringIO as StringIO
+except ImportError:
+    import StringIO
+import re
+import warnings
 
 __all__ = [
     'get_importer', 'iter_importers', 'get_loader', 'find_loader',
-    'walk_packages', 'iter_modules',
+    'walk_packages', 'iter_modules', 'get_data',
     'ImpImporter', 'ImpLoader', 'read_code', 'extend_path',
     'Distribution', 'EggInfoDistribution', 'distinfo_dirname',
     'get_distributions', 'get_distribution', 'get_file_users',
@@ -24,7 +31,7 @@
 
 
 def read_code(stream):
-    # This helper is needed in order for the PEP 302 emulation to
+    # This helper is needed in order for the :pep:`302` emulation to
     # correctly handle compiled files
     import marshal
 
@@ -79,32 +86,34 @@
 
 
 def walk_packages(path=None, prefix='', onerror=None):
-    """Yields (module_loader, name, ispkg) for all modules recursively
-    on path, or, if path is None, all accessible modules.
+    """Yields ``(module_loader, name, ispkg)`` for all modules recursively
+    on *path*, or, if *path* is ``None``, all accessible modules.
 
-    'path' should be either None or a list of paths to look for
-    modules in.
+    :parameter path: should be either ``None`` or a list of paths to look for
+                     modules in.
+    :parameter prefix: is a string to output on the front of every module name
+                       on output.
 
-    'prefix' is a string to output on the front of every module name
-    on output.
-
-    Note that this function must import all *packages* (NOT all
-    modules!) on the given path, in order to access the __path__
+    Note that this function must import all packages (NOT all
+    modules!) on the given path, in order to access the ``__path__``
     attribute to find submodules.
 
-    'onerror' is a function which gets called with one argument (the
+    *onerror* is a function which gets called with one argument (the
     name of the package which was being imported) if any exception
     occurs while trying to import a package.  If no onerror function is
-    supplied, ImportErrors are caught and ignored, while all other
+    supplied, ``ImportErrors`` are caught and ignored, while all other
     exceptions are propagated, terminating the search.
 
     Examples:
 
-    # list all modules python can access
-    walk_packages()
+    * list all modules python can access::
 
-    # list all submodules of ctypes
-    walk_packages(ctypes.__path__, ctypes.__name__+'.')
+        walk_packages()
+
+    * list all submodules of ctypes::
+
+        walk_packages(ctypes.__path__, ctypes.__name__+'.')
+
     """
 
     def seen(p, m={}):
@@ -137,14 +146,14 @@
 
 
 def iter_modules(path=None, prefix=''):
-    """Yields (module_loader, name, ispkg) for all submodules on path,
-    or, if path is None, all top-level modules on sys.path.
+    """Yields ``(module_loader, name, ispkg)`` for all submodules on path,
+    or, if *path* is ``None``, all top-level modules on ``sys.path``.
 
-    'path' should be either None or a list of paths to look for
-    modules in.
+    :parameter path: should be either None or a list of paths to look for
+                     modules in.
+    :parameter prefix: is a string to output on the front of every module name
+                       on output.
 
-    'prefix' is a string to output on the front of every module name
-    on output.
     """
 
     if path is None:
@@ -162,6 +171,7 @@
 
 #@simplegeneric
 def iter_importer_modules(importer, prefix=''):
+    ""
     if not hasattr(importer, 'iter_modules'):
         return []
     return importer.iter_modules(prefix)
@@ -169,15 +179,16 @@
 iter_importer_modules = simplegeneric(iter_importer_modules)
 
 
-class ImpImporter:
-    """PEP 302 Importer that wraps Python's "classic" import algorithm
+class ImpImporter(object):
+    """:pep:`302` Importer that wraps Python's "classic" import algorithm
 
-    ImpImporter(dirname) produces a PEP 302 importer that searches that
-    directory.  ImpImporter(None) produces a PEP 302 importer that searches
-    the current sys.path, plus any modules that are frozen or built-in.
+    ``ImpImporter(dirname)`` produces a :pep:`302` importer that searches that
+    directory. ``ImpImporter(None)`` produces a :pep:`302` importer that
+    searches the current ``sys.path``, plus any modules that are frozen
+    or built-in.
 
-    Note that ImpImporter does not currently support being used by placement
-    on sys.meta_path.
+    Note that :class:`ImpImporter` does not currently support being used by placement
+    on ``sys.meta_path``.
     """
 
     def __init__(self, path=None):
@@ -231,9 +242,9 @@
                 yield prefix + modname, ispkg
 
 
-class ImpLoader:
-    """PEP 302 Loader that wraps Python's "classic" import algorithm
-    """
+class ImpLoader(object):
+    """:pep:`302` Loader that wraps Python's "classic" import algorithm """
+
     code = source = None
 
     def __init__(self, fullname, file, filename, etc):
@@ -365,17 +376,17 @@
 
 
 def get_importer(path_item):
-    """Retrieve a PEP 302 importer for the given path item
+    """Retrieve a  :pep:`302` importer for the given path item
 
-    The returned importer is cached in sys.path_importer_cache
+    The returned importer is cached in ``sys.path_importer_cache``
     if it was newly created by a path hook.
 
     If there is no importer, a wrapper around the basic import
     machinery is returned. This wrapper is never inserted into
-    the importer cache (None is inserted instead).
+    the importer cache (``None`` is inserted instead).
 
     The cache (or part of it) can be cleared manually if a
-    rescan of sys.path_hooks is necessary.
+    rescan of ``sys.path_hooks`` is necessary.
     """
     try:
         importer = sys.path_importer_cache[path_item]
@@ -399,7 +410,7 @@
 
 
 def iter_importers(fullname=""):
-    """Yield PEP 302 importers for the given module name
+    """Yield :pep:`302` importers for the given module name
 
     If fullname contains a '.', the importers will be for the package
     containing fullname, otherwise they will be importers for sys.meta_path,
@@ -407,20 +418,20 @@
     the named module is in a package, that package is imported as a side
     effect of invoking this function.
 
-    Non PEP 302 mechanisms (e.g. the Windows registry) used by the
+    Non :pep:`302` mechanisms (e.g. the Windows registry) used by the
     standard import machinery to find files in alternative locations
-    are partially supported, but are searched AFTER sys.path. Normally,
-    these locations are searched BEFORE sys.path, preventing sys.path
+    are partially supported, but are searched AFTER ``sys.path``. Normally,
+    these locations are searched BEFORE sys.path, preventing ``sys.path``
     entries from shadowing them.
 
     For this to cause a visible difference in behaviour, there must
     be a module or package name that is accessible via both sys.path
-    and one of the non PEP 302 file system mechanisms. In this case,
+    and one of the non :pep:`302` file system mechanisms. In this case,
     the emulation will find the former version, while the builtin
     import mechanism will find the latter.
 
     Items of the following types can be affected by this discrepancy:
-        imp.C_EXTENSION, imp.PY_SOURCE, imp.PY_COMPILED, imp.PKG_DIRECTORY
+        ``imp.C_EXTENSION, imp.PY_SOURCE, imp.PY_COMPILED, imp.PKG_DIRECTORY``
     """
     if fullname.startswith('.'):
         raise ImportError("Relative module names not supported")
@@ -441,15 +452,15 @@
 
 
 def get_loader(module_or_name):
-    """Get a PEP 302 "loader" object for module_or_name
+    """Get a :pep:`302` "loader" object for module_or_name
 
     If the module or package is accessible via the normal import
     mechanism, a wrapper around the relevant part of that machinery
     is returned.  Returns None if the module cannot be found or imported.
     If the named module is not already imported, its containing package
-    (if any) is imported, in order to establish the package __path__.
+    (if any) is imported, in order to establish the package ``__path__``.
 
-    This function uses iter_importers(), and is thus subject to the same
+    This function uses :func:`iter_importers`, and is thus subject to the same
     limitations regarding platform-specific special import locations such
     as the Windows registry.
     """
@@ -467,12 +478,13 @@
 
 
 def find_loader(fullname):
-    """Find a PEP 302 "loader" object for fullname
+    """Find a :pep:`302` "loader" object for fullname
 
-    If fullname contains dots, path must be the containing package's __path__.
-    Returns None if the module cannot be found or imported. This function uses
-    iter_importers(), and is thus subject to the same limitations regarding
-    platform-specific special import locations such as the Windows registry.
+    If fullname contains dots, path must be the containing package's
+    ``__path__``. Returns ``None`` if the module cannot be found or imported.
+    This function uses :func:`iter_importers`, and is thus subject to the same
+    limitations regarding platform-specific special import locations such as
+    the Windows registry.
     """
     for importer in iter_importers(fullname):
         loader = importer.find_module(fullname)
@@ -485,21 +497,22 @@
 def extend_path(path, name):
     """Extend a package's path.
 
-    Intended use is to place the following code in a package's __init__.py:
+    Intended use is to place the following code in a package's
+    ``__init__.py``::
 
         from pkgutil import extend_path
         __path__ = extend_path(__path__, __name__)
 
-    This will add to the package's __path__ all subdirectories of
-    directories on sys.path named after the package.  This is useful
+    This will add to the package's ``__path__`` all subdirectories of
+    directories on ``sys.path`` named after the package.  This is useful
     if one wants to distribute different parts of a single logical
     package as multiple directories.
 
-    It also looks for *.pkg files beginning where * matches the name
-    argument.  This feature is similar to *.pth files (see site.py),
-    except that it doesn't special-case lines starting with 'import'.
-    A *.pkg file is trusted at face value: apart from checking for
-    duplicates, all entries found in a *.pkg file are added to the
+    It also looks for ``*.pkg`` files beginning where ``*`` matches the name
+    argument.  This feature is similar to ``*.pth`` files (see ``site.py``),
+    except that it doesn't special-case lines starting with ``import``.
+    A ``*.pkg`` file is trusted at face value: apart from checking for
+    duplicates, all entries found in a ``*.pkg`` file are added to the
     path, regardless of whether they are exist the filesystem.  (This
     is a feature.)
 
@@ -512,7 +525,7 @@
     are not (unicode or 8-bit) strings referring to existing
     directories are ignored.  Unicode items of sys.path that cause
     errors when used as filenames may cause this function to raise an
-    exception (in line with os.path.isdir() behavior).
+    exception (in line with ``os.path.isdir()`` behavior).
     """
 
     if not isinstance(path, list):
@@ -560,23 +573,23 @@
 def get_data(package, resource):
     """Get a resource from a package.
 
-    This is a wrapper round the PEP 302 loader get_data API. The package
+    This is a wrapper round the :pep:`302` loader get_data API. The package
     argument should be the name of a package, in standard module format
-    (foo.bar). The resource argument should be in the form of a relative
-    filename, using '/' as the path separator. The parent directory name '..'
-    is not allowed, and nor is a rooted name (starting with a '/').
+    (``foo.bar``). The resource argument should be in the form of a relative
+    filename, using ``'/'`` as the path separator. The parent directory name
+    ``'..'`` is not allowed, and nor is a rooted name (starting with a ``'/'``).
 
     The function returns a binary string, which is the contents of the
     specified resource.
 
     For packages located in the filesystem, which have already been imported,
-    this is the rough equivalent of
+    this is the rough equivalent of::
 
         d = os.path.dirname(sys.modules[package].__file__)
         data = open(os.path.join(d, resource), 'rb').read()
 
-    If the package cannot be located or loaded, or it uses a PEP 302 loader
-    which does not support get_data(), then None is returned.
+    If the package cannot be located or loaded, or it uses a :pep:`302` loader
+    which does not support :func:`get_data`, then ``None`` is returned.
     """
 
     loader = get_loader(package)
@@ -603,7 +616,7 @@
 
 class Distribution(object):
     """Created with the *path* of the ``.dist-info`` directory provided to the
-    constructor. It reads the metadata contained in METADATA when it is
+    constructor. It reads the metadata contained in ``METADATA`` when it is
     instantiated."""
 
     # Attribute documenting for Sphinx style documentation, see for more info:
@@ -612,9 +625,9 @@
     """The name of the distribution."""
     metadata = None
     """A :class:`distutils2.metadata.DistributionMetadata` instance loaded with
-    the distribution's METADATA file."""
+    the distribution's ``METADATA`` file."""
     requested = False
-    """A boolean that indicates whether the REQUESTED metadata file is present
+    """A boolean that indicates whether the ``REQUESTED`` metadata file is present
     (in other words, whether the package was installed by user request)."""
 
     def __init__(self, path):
@@ -635,15 +648,17 @@
 
     def get_installed_files(self, local=False):
         """
-        Iterates over the RECORD entries and returns a tuple (path, md5, size)
-        for each line. If *local* is ``True``, the returned path is transformed
-        into a local absolute path. Otherwise the raw value from RECORD is
-        returned.
+        Iterates over the ``RECORD`` entries and returns a tuple
+        ``(path, md5, size)`` for each line. If *local* is ``True``,
+        the returned path is transformed into a local absolute path.
+        Otherwise the raw value from RECORD is returned.
 
         A local absolute path is an absolute path in which occurrences of
         ``'/'`` have been replaced by the system separator given by ``os.sep``.
+
         :parameter local: flag to say if the path should be returned a local
                           absolute path
+
         :type local: boolean
         :returns: iterator of (path, md5, size)
         """
@@ -651,7 +666,7 @@
 
     def uses(self, path):
         """
-        Returns ``True`` if path is listed in RECORD. *path* can be a local
+        Returns ``True`` if path is listed in ``RECORD``. *path* can be a local
         absolute path or a relative ``'/'``-separated path.
 
         :rtype: boolean
@@ -673,8 +688,8 @@
                          directory path, a :class:`DistutilsError` is raised
         :type path: string
         :parameter binary: If *binary* is ``True``, opens the file in read-only
-                           binary mode (rb), otherwise opens it in read-only
-                           mode (r).
+                           binary mode (``rb``), otherwise opens it in read-only
+                           mode (``r``).
         :rtype: file object
         """
         open_flags = 'r'
@@ -701,37 +716,129 @@
 
     def get_distinfo_files(self, local=False):
         """
-        Iterates over the RECORD entries and returns paths for each line if the
-        path is pointing to a file located in the ``.dist-info`` directory or
-        one of its subdirectories.
+        Iterates over the ``RECORD`` entries and returns paths for each line if
+        the path is pointing to a file located in the ``.dist-info`` directory
+        or one of its subdirectories.
 
         :parameter local: If *local* is ``True``, each returned path is
                           transformed into a local absolute path. Otherwise the
-                          raw value from RECORD is returned.
+                          raw value from ``RECORD`` is returned.
         :type local: boolean
         :returns: iterator of paths
         """
         for path, md5, size in self._get_records(local):
             yield path
 
+    def __eq__(self, other):
+        return isinstance(other, Distribution) and self.path == other.path
+
+    # See http://docs.python.org/reference/datamodel#object.__hash__
+    __hash__ = object.__hash__
+
 
 class EggInfoDistribution(object):
     """Created with the *path* of the ``.egg-info`` directory or file provided
     to the constructor. It reads the metadata contained in the file itself, or
     if the given path happens to be a directory, the metadata is read from the
-    file PKG-INFO under that directory."""
+    file ``PKG-INFO`` under that directory."""
 
     name = ''
     """The name of the distribution."""
     metadata = None
     """A :class:`distutils2.metadata.DistributionMetadata` instance loaded with
-    the distribution's METADATA file."""
+    the distribution's ``METADATA`` file."""
+    _REQUIREMENT = re.compile( \
+        r'(?P<name>[-A-Za-z0-9_.]+)\s*' \
+        r'(?P<first>(?:<|<=|!=|==|>=|>)[-A-Za-z0-9_.]+)?\s*' \
+        r'(?P<rest>(?:\s*,\s*(?:<|<=|!=|==|>=|>)[-A-Za-z0-9_.]+)*)\s*' \
+        r'(?P<extras>\[.*\])?')
 
     def __init__(self, path):
-        if os.path.isdir(path):
-            path = os.path.join(path, 'PKG-INFO')
-        self.metadata = DistributionMetadata(path=path)
-        self.name = self.metadata['name']
+        self.path = path
+
+        # reused from Distribute's pkg_resources
+        def yield_lines(strs):
+            """Yield non-empty/non-comment lines of a ``basestring`` or sequence"""
+            if isinstance(strs, basestring):
+                for s in strs.splitlines():
+                    s = s.strip()
+                    if s and not s.startswith('#'): # skip blank lines/comments
+                        yield s
+            else:
+                for ss in strs:
+                    for s in yield_lines(ss):
+                        yield s
+
+        requires = None
+        if path.endswith('.egg'):
+            if os.path.isdir(path):
+                meta_path = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
+                self.metadata = DistributionMetadata(path=meta_path)
+                try:
+                    req_path = os.path.join(path, 'EGG-INFO', 'requires.txt')
+                    requires = open(req_path, 'r').read()
+                except IOError:
+                    requires = None
+            else:
+                zipf = zipimport.zipimporter(path)
+                fileobj = StringIO.StringIO(zipf.get_data('EGG-INFO/PKG-INFO'))
+                self.metadata = DistributionMetadata(fileobj=fileobj)
+                try:
+                    requires = zipf.get_data('EGG-INFO/requires.txt')
+                except IOError:
+                    requires = None
+            self.name = self.metadata['name']
+        elif path.endswith('.egg-info'):
+            if os.path.isdir(path):
+                path = os.path.join(path, 'PKG-INFO')
+                try:
+                    req_f = open(os.path.join(path, 'requires.txt'), 'r')
+                    requires = req_f.read()
+                except IOError:
+                    requires = None
+            self.metadata = DistributionMetadata(path=path)
+            self.name = self.metadata['name']
+        else:
+            raise ValueError('The path must end with .egg-info or .egg')
+
+        provides = "%s (%s)" % (self.metadata['name'],
+                                self.metadata['version'])
+        if self.metadata['Metadata-Version'] == '1.2':
+            self.metadata['Provides-Dist'] += (provides,)
+        else:
+            self.metadata['Provides'] += (provides,)
+        reqs = []
+        if requires is not None:
+            for line in yield_lines(requires):
+                if line[0] == '[':
+                    warnings.warn('distutils2 does not support extensions in requires.txt')
+                    break
+                else:
+                    match = self._REQUIREMENT.match(line.strip())
+                    if not match:
+                        raise ValueError('Distribution %s has ill formed '
+                                         'requires.txt file (%s)' %
+                                         (self.name, line))
+                    else:
+                        if match.group('extras'):
+                            s = 'Distribution %s uses extra requirements which'\
+                                ' are not supported in distutils' % (self.name)
+                            warnings.warn(s)
+                        name = match.group('name')
+                        version = None
+                        if match.group('first'):
+                            version = match.group('first')
+                            if match.group('rest'):
+                                version += match.group('rest')
+                            version = version.replace(' ', '') # trim spaces
+                        if version is None:
+                            reqs.append(name)
+                        else:
+                            reqs.append('%s (%s)' % (name, version))
+            if self.metadata['Metadata-Version'] == '1.2':
+                self.metadata['Requires-Dist'] += reqs
+            else:
+                self.metadata['Requires'] += reqs
 
     def get_installed_files(self, local=False):
         return []
@@ -739,6 +846,13 @@
     def uses(self, path):
         return False
 
+    def __eq__(self, other):
+        return isinstance(other, EggInfoDistribution) and \
+               self.path == other.path
+
+    # See http://docs.python.org/reference/datamodel#object.__hash__
+    __hash__ = object.__hash__
+
 
 def _normalize_dist_name(name):
     """Returns a normalized name from the given *name*.
@@ -792,7 +906,8 @@
             if dir.endswith('.dist-info'):
                 dist = Distribution(os.path.join(realpath, dir))
                 yield dist
-            elif use_egg_info and dir.endswith('.egg-info'):
+            elif use_egg_info and (dir.endswith('.egg-info') or
+                                   dir.endswith('.egg')):
                 dist = EggInfoDistribution(os.path.join(realpath, dir))
                 yield dist
 
@@ -801,18 +916,18 @@
     """
     Scans all elements in ``sys.path`` and looks for all directories ending with
     ``.dist-info``. Returns a :class:`Distribution` corresponding to the
-    ``.dist-info`` directory that contains the METADATA that matches *name* for
-    the *name* metadata field.
+    ``.dist-info`` directory that contains the ``METADATA`` that matches *name*
+    for the *name* metadata field.
     If no distribution exists with the given *name* and the parameter
     *use_egg_info* is set to ``True``, then all files and directories ending
     with ``.egg-info`` are scanned. A :class:`EggInfoDistribution` instance is
     returned if one is found that has metadata that matches *name* for the
     *name* metadata field.
 
-    This function only returns the first result founded, as no more than one
+    This function only returns the first result found, as no more than one
     value is expected. If the directory is not found, ``None`` is returned.
 
-    :rtype: :class:`Distribution` or :class:`EggInfoDistribution: or None"""
+    :rtype: :class:`Distribution` or :class:`EggInfoDistribution` or None"""
     found = None
     for dist in get_distributions():
         if dist.name == name:
@@ -867,7 +982,7 @@
     then all files and directories ending with ``.egg-info`` are considered
     as well and returns an :class:`EggInfoDistribution` instance.
 
-    This function only returns the first result founded, since no more than
+    This function only returns the first result found, since no more than
     one values are expected. If the directory is not found, returns ``None``.
 
     :parameter version: a version specifier that indicates the version
@@ -887,7 +1002,7 @@
         provided = dist.metadata['Provides-Dist'] + dist.metadata['Provides']
 
         for p in provided:
-            p_components = p.split(' ', 1)
+            p_components = p.rsplit(' ', 1)
             if len(p_components) == 1 or predicate is None:
                 if name == p_components[0]:
                     yield dist
@@ -896,7 +1011,8 @@
                 p_name, p_ver = p_components
                 if len(p_ver) < 2 or p_ver[0] != '(' or p_ver[-1] != ')':
                     raise DistutilsError(('Distribution %s has invalid ' +
-                                          'provides field') % (dist.name,))
+                                          'provides field: %s') \
+                                           % (dist.name, p))
                 p_ver = p_ver[1:-1] # trim off the parenthesis
                 if p_name == name and predicate.match(p_ver):
                     yield dist
diff --git a/src/distutils2/_backport/sysconfig.cfg b/src/distutils2/_backport/sysconfig.cfg
--- a/src/distutils2/_backport/sysconfig.cfg
+++ b/src/distutils2/_backport/sysconfig.cfg
@@ -1,6 +1,6 @@
 [globals]
 # These are the useful categories that are sometimes referenced at runtime,
-# using pkgutils.open():
+# using pkgutil.open():
 config             = {confdir}/{distribution.name}         # Configuration files
 appdata            = {datadir}/{distribution.name}         # Non-writable data that is independent of architecture (images, many xml/text files)
 appdata.arch       = {libdir}/{distribution.name}          # Non-writable data that is architecture-dependent (some binary data formats)
diff --git a/src/distutils2/_backport/sysconfig.py b/src/distutils2/_backport/sysconfig.py
--- a/src/distutils2/_backport/sysconfig.py
+++ b/src/distutils2/_backport/sysconfig.py
@@ -5,7 +5,7 @@
 import os
 import re
 from os.path import pardir, abspath
-from ConfigParser import ConfigParser
+from ConfigParser import RawConfigParser
 
 _PREFIX = os.path.normpath(sys.prefix)
 _EXEC_PREFIX = os.path.normpath(sys.exec_prefix)
@@ -14,7 +14,7 @@
 # XXX _CONFIG_DIR will be set by the Makefile later
 _CONFIG_DIR = os.path.normpath(os.path.dirname(__file__))
 _CONFIG_FILE = os.path.join(_CONFIG_DIR, 'sysconfig.cfg')
-_SCHEMES = ConfigParser()
+_SCHEMES = RawConfigParser()
 _SCHEMES.read(_CONFIG_FILE)
 _VAR_REPL = re.compile(r'\{([^{]*?)\}')
 
@@ -580,10 +580,11 @@
                 # behaviour.
                 pass
             else:
-                m = re.search(
-                        r'<key>ProductUserVisibleVersion</key>\s*' +
-                        r'<string>(.*?)</string>', f.read())
-                f.close()
+                try:
+                    m = re.search(r'<key>ProductUserVisibleVersion</key>\s*'
+                                  r'<string>(.*?)</string>', f.read())
+                finally:
+                    f.close()
                 if m is not None:
                     macrelease = '.'.join(m.group(1).split('.')[:2])
                 # else: fall back to the default behaviour
diff --git a/src/distutils2/_backport/tarfile.py b/src/distutils2/_backport/tarfile.py
--- a/src/distutils2/_backport/tarfile.py
+++ b/src/distutils2/_backport/tarfile.py
@@ -370,7 +370,7 @@
 #---------------------------
 # internal stream interface
 #---------------------------
-class _LowLevelFile:
+class _LowLevelFile(object):
     """Low-level file object. Supports reading and writing.
        It is used instead of a regular file object for streaming
        access.
@@ -394,7 +394,7 @@
     def write(self, s):
         os.write(self.fd, s)
 
-class _Stream:
+class _Stream(object):
     """Class that serves as an adapter between TarFile and
        a stream-like object.  The stream-like object only
        needs to have a read() or write() method and is accessed
@@ -2003,8 +2003,10 @@
         # Append the tar header and data to the archive.
         if tarinfo.isreg():
             f = bltn_open(name, "rb")
-            self.addfile(tarinfo, f)
-            f.close()
+            try:
+                self.addfile(tarinfo, f)
+            finally:
+                f.close()
 
         elif tarinfo.isdir():
             self.addfile(tarinfo)
@@ -2214,9 +2216,11 @@
         """
         source = self.extractfile(tarinfo)
         target = bltn_open(targetpath, "wb")
-        copyfileobj(source, target)
-        source.close()
-        target.close()
+        try:
+            copyfileobj(source, target)
+        finally:
+            source.close()
+            target.close()
 
     def makeunknown(self, tarinfo, targetpath):
         """Make a file from a TarInfo object with an unknown type
@@ -2423,7 +2427,7 @@
             print >> sys.stderr, msg
 # class TarFile
 
-class TarIter:
+class TarIter(object):
     """Iterator Class.
 
        for tarinfo in TarFile(...):
@@ -2460,7 +2464,7 @@
         return tarinfo
 
 # Helper classes for sparse file support
-class _section:
+class _section(object):
     """Base class for _data and _hole.
     """
     def __init__(self, offset, size):
@@ -2507,7 +2511,7 @@
 #---------------------------------------------
 TAR_PLAIN = 0           # zipfile.ZIP_STORED
 TAR_GZIPPED = 8         # zipfile.ZIP_DEFLATED
-class TarFileCompat:
+class TarFileCompat(object):
     """TarFile class compatible with standard module zipfile's
        ZipFile class.
     """
@@ -2564,8 +2568,10 @@
        are able to handle, else return False.
     """
     try:
-        t = open(name)
-        t.close()
+        try:
+            t = open(name)
+        finally:
+            t.close()
         return True
     except TarError:
         return False
diff --git a/src/distutils2/_backport/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO b/src/distutils2/_backport/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO
--- a/src/distutils2/_backport/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO
+++ b/src/distutils2/_backport/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO
@@ -2,4 +2,5 @@
 Name: bacon
 Version: 0.1
 Provides-Dist: truffles (2.0)
+Provides-Dist: bacon (0.1)
 Obsoletes-Dist: truffles (>=0.9,<=1.5)
diff --git a/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/PKG-INFO b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/PKG-INFO
new file mode 100644
--- /dev/null
+++ b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/PKG-INFO
@@ -0,0 +1,18 @@
+Metadata-Version: 1.0
+Name: banana
+Version: 0.4
+Summary: A yellow fruit
+Home-page: http://en.wikipedia.org/wiki/Banana
+Author: Josip Djolonga
+Author-email: foo at nbar.com
+License: BSD
+Description: A fruit
+Keywords: foo bar
+Platform: UNKNOWN
+Classifier: Development Status :: 4 - Beta
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Science/Research
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Scientific/Engineering :: GIS
diff --git a/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/SOURCES.txt b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/SOURCES.txt
new file mode 100644
diff --git a/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/dependency_links.txt b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/dependency_links.txt
new file mode 100644
--- /dev/null
+++ b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/dependency_links.txt
@@ -0,0 +1,1 @@
+
diff --git a/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/entry_points.txt b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/entry_points.txt
new file mode 100644
--- /dev/null
+++ b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/entry_points.txt
@@ -0,0 +1,3 @@
+
+      # -*- Entry points: -*-
+      
\ No newline at end of file
diff --git a/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/not-zip-safe b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/not-zip-safe
new file mode 100644
--- /dev/null
+++ b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/not-zip-safe
@@ -0,0 +1,1 @@
+
diff --git a/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/requires.txt b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/requires.txt
new file mode 100644
--- /dev/null
+++ b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/requires.txt
@@ -0,0 +1,6 @@
+# this should be ignored
+
+strawberry >=0.5
+
+[section ignored]
+foo ==0.5
diff --git a/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/top_level.txt b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/top_level.txt
new file mode 100644
diff --git a/src/distutils2/_backport/tests/fake_dists/strawberry-0.6.egg b/src/distutils2/_backport/tests/fake_dists/strawberry-0.6.egg
new file mode 100644
index 0000000000000000000000000000000000000000..6d160e8b161031ae52638514843592187925b757
GIT binary patch
literal 1402
zc$`yK)KALH(=X28%1l#;R!B%nEKbc!%uQ8LF-TCbRZuEUEh#N1$<NOz)-}*GOExsr
zEvPioGuBH at Pghci<toWY%~MFNNKIBKs4U6I&jU&+=q4*DW#$&-7nLX!R~G9i<QH3m
z7<vVXB^i2|dBv$kB^m}GVTed#QZb0uP**Wf*VMFDNGr<ERX|v)mz<xQo0ylPmzr2y
z84uK6l9-dD05v};Kfk27q$sffVnb?0W{Cz|OhZ#sNkK`)L5V9hr#O{MK_N9cBOmB5
zh0J1wy!;XcpxYIa^NWg7lS>r9UQWv|0ty0Ufu2)H%gjmDgJ}xL0otCbPy`8 at OrXVy
z$=M1e`3iV~M*-+)g_5F5g~as4%sjABpm0h{1UV)xlPkcRnT3l11j?rGw_!j6Vhl12
zuI}!-o_=or`X%`V at j0nwsX2Nj6(yk|oD9qiubF*7xU_<sfsy3}GXn#dK$usBW}XPL
zdBOgnLC&thaML(|CUIalO$4ZygTbxvn9cJCa*K8Yd7pq-5ZSZ<ZwLcyl*$Bd9}q?}
z%GU3+-(d%yJ- at a8FngYAJv`50)he!AtAbSdVkX?4u~u<++)e34e{3~_X5O|jF|J;B
zC3xwx-m|X72inV6*}Y^gtiD*1wpsGu-O%40%MDdGSw1j37$$7q`bcR1wJ4|Sg?Z{f
zrybfGuv%=l^lLM>i9RnLxBqqeU~_GC)B*8Q%^m+PITr6Z&8t31F+o4>xK^{d-p7?^
zx~J#&ZkWuS9<SEFoOjj3--Yb2mtJ3TW&iZ at 1KQ^&N4;mX5B=!V_JJD_rYtj1!&DME
z6jJkm at f=@}pP5%u3=ddwV4#ZQ4p;_;F99Leli98tF#_2jEDOYNBU4ffQu9($^O7s$
zb29U?!NvlU3?q{qGp<A<0cUS%tYT!qnS at v&NeIJT2(vL05VF~)kj+L(POOmRgw<qR
zi3wsdBiLjKCI)OtixrZz at HiMVb`TC;(kREwfG at 6CA#sJpco7ENF@*?^LS_Q-7U0dw
Q22#ojgxi7o*D`~60Pqom)c^nh

diff --git a/src/distutils2/_backport/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA b/src/distutils2/_backport/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA
--- a/src/distutils2/_backport/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA
+++ b/src/distutils2/_backport/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA
@@ -2,4 +2,6 @@
 Name: towel-stuff
 Version: 0.1
 Provides-Dist: truffles (1.1.2)
+Provides-Dist: towel-stuff (0.1)
 Obsoletes-Dist: truffles (!=0.8,<1.0)
+Requires-Dist: bacon (<=0.2)
diff --git a/src/distutils2/_backport/tests/test_pkgutil.py b/src/distutils2/_backport/tests/test_pkgutil.py
--- a/src/distutils2/_backport/tests/test_pkgutil.py
+++ b/src/distutils2/_backport/tests/test_pkgutil.py
@@ -3,40 +3,176 @@
 import sys
 import os
 import csv
+import imp
+import tempfile
+import shutil
+import zipfile
 try:
     from hashlib import md5
 except ImportError:
     from md5 import md5
 
 from test.test_support import run_unittest, TESTFN
-
-import distutils2._backport.pkgutil
 from distutils2.tests.support import unittest
 
+from distutils2._backport import pkgutil
+
+try:
+    from os.path import relpath
+except ImportError:
+    try:
+        from unittest.compatibility import relpath
+    except ImportError:
+        from unittest2.compatibility import relpath
+
 # TODO Add a test for getting a distribution that is provided by another
-#   distribution.
+# distribution.
 
 # TODO Add a test for absolute pathed RECORD items (e.g. /etc/myapp/config.ini)
 
+# Adapted from Python 2.7's trunk
+class TestPkgUtilData(unittest.TestCase):
+
+    def setUp(self):
+        super(TestPkgUtilData, self).setUp()
+        self.dirname = tempfile.mkdtemp()
+        sys.path.insert(0, self.dirname)
+
+    def tearDown(self):
+        super(TestPkgUtilData, self).tearDown()
+        del sys.path[0]
+        shutil.rmtree(self.dirname)
+
+    def test_getdata_filesys(self):
+        pkg = 'test_getdata_filesys'
+
+        # Include a LF and a CRLF, to test that binary data is read back
+        RESOURCE_DATA = 'Hello, world!\nSecond line\r\nThird line'
+
+        # Make a package with some resources
+        package_dir = os.path.join(self.dirname, pkg)
+        os.mkdir(package_dir)
+        # Empty init.py
+        f = open(os.path.join(package_dir, '__init__.py'), "wb")
+        try:
+            pass
+        finally:
+            f.close()
+        # Resource files, res.txt, sub/res.txt
+        f = open(os.path.join(package_dir, 'res.txt'), "wb")
+        try:
+            f.write(RESOURCE_DATA)
+        finally:
+            f.close()
+        os.mkdir(os.path.join(package_dir, 'sub'))
+        f = open(os.path.join(package_dir, 'sub', 'res.txt'), "wb")
+        try:
+            f.write(RESOURCE_DATA)
+        finally:
+            f.close()
+
+        # Check we can read the resources
+        res1 = pkgutil.get_data(pkg, 'res.txt')
+        self.assertEqual(res1, RESOURCE_DATA)
+        res2 = pkgutil.get_data(pkg, 'sub/res.txt')
+        self.assertEqual(res2, RESOURCE_DATA)
+
+        del sys.modules[pkg]
+
+    def test_getdata_zipfile(self):
+        zip = 'test_getdata_zipfile.zip'
+        pkg = 'test_getdata_zipfile'
+
+        # Include a LF and a CRLF, to test that binary data is read back
+        RESOURCE_DATA = 'Hello, world!\nSecond line\r\nThird line'
+
+        # Make a package with some resources
+        zip_file = os.path.join(self.dirname, zip)
+        z = zipfile.ZipFile(zip_file, 'w')
+        try:
+            # Empty init.py
+            z.writestr(pkg + '/__init__.py', "")
+            # Resource files, res.txt, sub/res.txt
+            z.writestr(pkg + '/res.txt', RESOURCE_DATA)
+            z.writestr(pkg + '/sub/res.txt', RESOURCE_DATA)
+        finally:
+            z.close()
+
+        # Check we can read the resources
+        sys.path.insert(0, zip_file)
+        res1 = pkgutil.get_data(pkg, 'res.txt')
+        self.assertEqual(res1, RESOURCE_DATA)
+        res2 = pkgutil.get_data(pkg, 'sub/res.txt')
+        self.assertEqual(res2, RESOURCE_DATA)
+        del sys.path[0]
+
+        del sys.modules[pkg]
+
+# Adapted from Python 2.7's trunk
+class TestPkgUtilPEP302(unittest.TestCase):
+
+    class MyTestLoader(object):
+        def load_module(self, fullname):
+            # Create an empty module
+            mod = sys.modules.setdefault(fullname, imp.new_module(fullname))
+            mod.__file__ = "<%s>" % self.__class__.__name__
+            mod.__loader__ = self
+            # Make it a package
+            mod.__path__ = []
+            # Count how many times the module is reloaded
+            mod.__dict__['loads'] = mod.__dict__.get('loads',0) + 1
+            return mod
+
+        def get_data(self, path):
+            return "Hello, world!"
+
+    class MyTestImporter(object):
+        def find_module(self, fullname, path=None):
+            return TestPkgUtilPEP302.MyTestLoader()
+
+    def setUp(self):
+        super(TestPkgUtilPEP302, self).setUp()
+        sys.meta_path.insert(0, self.MyTestImporter())
+
+    def tearDown(self):
+        del sys.meta_path[0]
+        super(TestPkgUtilPEP302, self).tearDown()
+
+    def test_getdata_pep302(self):
+        # Use a dummy importer/loader
+        self.assertEqual(pkgutil.get_data('foo', 'dummy'), "Hello, world!")
+        del sys.modules['foo']
+
+    def test_alreadyloaded(self):
+        # Ensure that get_data works without reloading - the "loads" module
+        # variable in the example loader should count how many times a reload
+        # occurs.
+        import foo
+        self.assertEqual(foo.loads, 1)
+        self.assertEqual(pkgutil.get_data('foo', 'dummy'), "Hello, world!")
+        self.assertEqual(foo.loads, 1)
+        del sys.modules['foo']
+
+
 class TestPkgUtilDistribution(unittest.TestCase):
-    """Tests the pkgutil.Distribution class"""
+    # Tests the pkgutil.Distribution class
 
     def setUp(self):
         super(TestPkgUtilDistribution, self).setUp()
-
         self.fake_dists_path = os.path.abspath(
             os.path.join(os.path.dirname(__file__), 'fake_dists'))
-        self.distinfo_dirs = [ os.path.join(self.fake_dists_path, dir)
+
+        self.distinfo_dirs = [os.path.join(self.fake_dists_path, dir)
             for dir in os.listdir(self.fake_dists_path)
-            if dir.endswith('.dist-info')
-            ]
+            if dir.endswith('.dist-info')]
 
         def get_hexdigest(file):
             md5_hash = md5()
             md5_hash.update(open(file).read())
             return md5_hash.hexdigest()
+
         def record_pieces(file):
-            path = os.path.relpath(file, sys.prefix)
+            path = relpath(file, sys.prefix)
             digest = get_hexdigest(file)
             size = os.path.getsize(file)
             return [path, digest, size]
@@ -51,16 +187,18 @@
 
             for path, dirs, files in os.walk(dist_location):
                 for f in files:
-                    record_writer.writerow(record_pieces(os.path.join(path, f)))
+                    record_writer.writerow(record_pieces(
+                                           os.path.join(path, f)))
             for file in ['INSTALLER', 'METADATA', 'REQUESTED']:
                 record_writer.writerow(record_pieces(
                     os.path.join(distinfo_dir, file)))
-            record_writer.writerow([os.path.relpath(record_file, sys.prefix)])
+            record_writer.writerow([relpath(record_file, sys.prefix)])
             del record_writer # causes the RECORD file to close
             record_reader = csv.reader(open(record_file, 'rb'))
             record_data = []
             for row in record_reader:
-                path, md5_, size = row[:] + [ None for i in xrange(len(row), 3) ]
+                path, md5_, size = row[:] + \
+                                   [None for i in xrange(len(row), 3)]
                 record_data.append([path, (md5_, size,)])
             self.records[distinfo_dir] = dict(record_data)
 
@@ -72,8 +210,8 @@
         super(TestPkgUtilDistribution, self).tearDown()
 
     def test_instantiation(self):
-        """Test the Distribution class's instantiation provides us with usable
-        attributes."""
+        # Test the Distribution class's instantiation provides us with usable
+        # attributes.
         # Import the Distribution class
         from distutils2._backport.pkgutil import distinfo_dirname, Distribution
 
@@ -91,7 +229,7 @@
         self.assertTrue(isinstance(dist.requested, type(bool())))
 
     def test_installed_files(self):
-        """Test the iteration of installed files."""
+        # Test the iteration of installed files.
         # Test the distribution's installed files
         from distutils2._backport.pkgutil import Distribution
         for distinfo_dir in self.distinfo_dirs:
@@ -103,16 +241,17 @@
                 self.assertEqual(size, record_data[path][1])
 
     def test_uses(self):
-        """Test to determine if a distribution uses a specified file."""
+        # Test to determine if a distribution uses a specified file.
         # Criteria to test against
         distinfo_name = 'grammar-1.0a4'
         distinfo_dir = os.path.join(self.fake_dists_path,
             distinfo_name + '.dist-info')
-        true_path = [self.fake_dists_path, distinfo_name, 'grammar', 'utils.py']
-        true_path = os.path.relpath(os.path.join(*true_path), sys.prefix)
+        true_path = [self.fake_dists_path, distinfo_name, \
+                     'grammar', 'utils.py']
+        true_path = relpath(os.path.join(*true_path), sys.prefix)
         false_path = [self.fake_dists_path, 'towel_stuff-0.1', 'towel_stuff',
             '__init__.py']
-        false_path = os.path.relpath(os.path.join(*false_path), sys.prefix)
+        false_path = relpath(os.path.join(*false_path), sys.prefix)
 
         # Test if the distribution uses the file in question
         from distutils2._backport.pkgutil import Distribution
@@ -121,7 +260,7 @@
         self.assertFalse(dist.uses(false_path))
 
     def test_get_distinfo_file(self):
-        """Test the retrieval of dist-info file objects."""
+        # Test the retrieval of dist-info file objects.
         from distutils2._backport.pkgutil import Distribution
         distinfo_name = 'choxie-2.0.0.9'
         other_distinfo_name = 'grammar-1.0a4'
@@ -150,10 +289,11 @@
         self.assertRaises(DistutilsError, dist.get_distinfo_file,
             other_distinfo_file)
         # Test for a file that does not exist and should not exist
-        self.assertRaises(DistutilsError, dist.get_distinfo_file, 'ENTRYPOINTS')
+        self.assertRaises(DistutilsError, dist.get_distinfo_file, \
+                          'ENTRYPOINTS')
 
     def test_get_distinfo_files(self):
-        """Test for the iteration of RECORD path entries."""
+        # Test for the iteration of RECORD path entries.
         from distutils2._backport.pkgutil import Distribution
         distinfo_name = 'towel_stuff-0.1'
         distinfo_dir = os.path.join(self.fake_dists_path,
@@ -161,21 +301,20 @@
         dist = Distribution(distinfo_dir)
         # Test for the iteration of the raw path
         distinfo_record_paths = self.records[distinfo_dir].keys()
-        found = [ path for path in dist.get_distinfo_files() ]
+        found = [path for path in dist.get_distinfo_files()]
         self.assertEqual(sorted(found), sorted(distinfo_record_paths))
         # Test for the iteration of local absolute paths
-        distinfo_record_paths = [ os.path.join(sys.prefix, path)
-            for path in self.records[distinfo_dir].keys()
-            ]
-        found = [ path for path in dist.get_distinfo_files(local=True) ]
+        distinfo_record_paths = [os.path.join(sys.prefix, path)
+            for path in self.records[distinfo_dir].keys()]
+        found = [path for path in dist.get_distinfo_files(local=True)]
         self.assertEqual(sorted(found), sorted(distinfo_record_paths))
 
 
-class TestPkgUtilFunctions(unittest.TestCase):
-    """Tests for the new functionality added in PEP 376."""
+class TestPkgUtilPEP376(unittest.TestCase):
+    # Tests for the new functionality added in PEP 376.
 
     def setUp(self):
-        super(TestPkgUtilFunctions, self).setUp()
+        super(TestPkgUtilPEP376, self).setUp()
         # Setup the path environment with our fake distributions
         current_path = os.path.abspath(os.path.dirname(__file__))
         self.sys_path = sys.path[:]
@@ -183,18 +322,19 @@
         sys.path[0:0] = [self.fake_dists_path]
 
     def tearDown(self):
-        super(TestPkgUtilFunctions, self).tearDown()
         sys.path[:] = self.sys_path
+        super(TestPkgUtilPEP376, self).tearDown()
 
     def test_distinfo_dirname(self):
-        """Given a name and a version, we expect the distinfo_dirname function
-        to return a standard distribution information directory name."""
+        # Given a name and a version, we expect the distinfo_dirname function
+        # to return a standard distribution information directory name.
 
-        items = [ # (name, version, standard_dirname)
-            # Test for a very simple single word name and decimal version number
+        items = [# (name, version, standard_dirname)
+            # Test for a very simple single word name and decimal
+            # version number
             ('docutils', '0.5', 'docutils-0.5.dist-info'),
             # Test for another except this time with a '-' in the name, which
-            #   needs to be transformed during the name lookup
+            # needs to be transformed during the name lookup
             ('python-ldap', '2.5', 'python_ldap-2.5.dist-info'),
             # Test for both '-' in the name and a funky version number
             ('python-ldap', '2.5 a---5', 'python_ldap-2.5 a---5.dist-info'),
@@ -209,7 +349,7 @@
             self.assertEqual(dirname, standard_dirname)
 
     def test_get_distributions(self):
-        """Lookup all distributions found in the ``sys.path``."""
+        # Lookup all distributions found in the ``sys.path``.
         # This test could potentially pick up other installed distributions
         fake_dists = [('grammar', '1.0a4'), ('choxie', '2.0.0.9'),
             ('towel-stuff', '0.1')]
@@ -221,7 +361,7 @@
                                                  EggInfoDistribution
 
         # Verify the fake dists have been found.
-        dists = [ dist for dist in get_distributions() ]
+        dists = [dist for dist in get_distributions()]
         for dist in dists:
             if not isinstance(dist, Distribution):
                 self.fail("item received was not a Distribution instance: "
@@ -234,10 +374,11 @@
         self.assertListEqual(sorted(found_dists), sorted(fake_dists))
 
         # Now, test if the egg-info distributions are found correctly as well
-        fake_dists += [('bacon', '0.1'), ('cheese', '2.0.2')]
+        fake_dists += [('bacon', '0.1'), ('cheese', '2.0.2'),
+                       ('banana', '0.4'), ('strawberry', '0.6')]
         found_dists = []
 
-        dists = [ dist for dist in get_distributions(use_egg_info=True) ]
+        dists = [dist for dist in get_distributions(use_egg_info=True)]
         for dist in dists:
             if not (isinstance(dist, Distribution) or \
                     isinstance(dist, EggInfoDistribution)):
@@ -248,9 +389,8 @@
 
         self.assertListEqual(sorted(fake_dists), sorted(found_dists))
 
-
     def test_get_distribution(self):
-        """Test for looking up a distribution by name."""
+        # Test for looking up a distribution by name.
         # Test the lookup of the towel-stuff distribution
         name = 'towel-stuff' # Note: This is different from the directory name
 
@@ -274,6 +414,8 @@
         # instructed to
         self.assertEqual(None, get_distribution('bacon'))
         self.assertEqual(None, get_distribution('cheese'))
+        self.assertEqual(None, get_distribution('strawberry'))
+        self.assertEqual(None, get_distribution('banana'))
 
         # Now check that it works well in both situations, when egg-info
         # is a file and directory respectively.
@@ -285,8 +427,16 @@
         self.assertTrue(isinstance(dist, EggInfoDistribution))
         self.assertEqual(dist.name, 'bacon')
 
+        dist = get_distribution('banana', use_egg_info=True)
+        self.assertTrue(isinstance(dist, EggInfoDistribution))
+        self.assertEqual(dist.name, 'banana')
+
+        dist = get_distribution('strawberry', use_egg_info=True)
+        self.assertTrue(isinstance(dist, EggInfoDistribution))
+        self.assertEqual(dist.name, 'strawberry')
+
     def test_get_file_users(self):
-        """Test the iteration of distributions that use a file."""
+        # Test the iteration of distributions that use a file.
         from distutils2._backport.pkgutil import get_file_users, Distribution
         name = 'towel_stuff-0.1'
         path = os.path.join(self.fake_dists_path, name,
@@ -296,11 +446,11 @@
             self.assertEqual(dist.name, name)
 
     def test_provides(self):
-        """ Test for looking up distributions by what they provide """
+        # Test for looking up distributions by what they provide
         from distutils2._backport.pkgutil import provides_distribution
         from distutils2.errors import DistutilsError
 
-        checkLists = lambda x,y: self.assertListEqual(sorted(x), sorted(y))
+        checkLists = lambda x, y: self.assertListEqual(sorted(x), sorted(y))
 
         l = [dist.name for dist in provides_distribution('truffles')]
         checkLists(l, ['choxie', 'towel-stuff'])
@@ -318,10 +468,12 @@
         l = [dist.name for dist in provides_distribution('truffles', '1.1')]
         checkLists(l, ['towel-stuff'])
 
-        l = [dist.name for dist in provides_distribution('truffles', '!=1.1,<=2.0')]
+        l = [dist.name for dist in provides_distribution('truffles', \
+                                                         '!=1.1,<=2.0')]
         checkLists(l, ['choxie'])
 
-        l = [dist.name for dist in provides_distribution('truffles', '!=1.1,<=2.0',
+        l = [dist.name for dist in provides_distribution('truffles', \
+                                                         '!=1.1,<=2.0',
                                                           use_egg_info=True)]
         checkLists(l, ['choxie', 'bacon', 'cheese'])
 
@@ -338,12 +490,39 @@
         l = [dist.name for dist in provides_distribution('truffles', '>=1.0')]
         checkLists(l, ['choxie', 'towel-stuff'])
 
+        l = [dist.name for dist in provides_distribution('strawberry', '0.6',
+                                                         use_egg_info=True)]
+        checkLists(l, ['strawberry'])
+
+        l = [dist.name for dist in provides_distribution('strawberry', '>=0.5',
+                                                         use_egg_info=True)]
+        checkLists(l, ['strawberry'])
+
+
+        l = [dist.name for dist in provides_distribution('strawberry', '>0.6',
+                                                         use_egg_info=True)]
+        checkLists(l, [])
+
+
+        l = [dist.name for dist in provides_distribution('banana', '0.4',
+                                                         use_egg_info=True)]
+        checkLists(l, ['banana'])
+
+        l = [dist.name for dist in provides_distribution('banana', '>=0.3',
+                                                         use_egg_info=True)]
+        checkLists(l, ['banana'])
+
+
+        l = [dist.name for dist in provides_distribution('banana', '!=0.4',
+                                                         use_egg_info=True)]
+        checkLists(l, [])
+
     def test_obsoletes(self):
-        """ Test looking for distributions based on what they obsolete """
+        # Test looking for distributions based on what they obsolete
         from distutils2._backport.pkgutil import obsoletes_distribution
         from distutils2.errors import DistutilsError
 
-        checkLists = lambda x,y: self.assertListEqual(sorted(x), sorted(y))
+        checkLists = lambda x, y: self.assertListEqual(sorted(x), sorted(y))
 
         l = [dist.name for dist in obsoletes_distribution('truffles', '1.0')]
         checkLists(l, [])
@@ -363,7 +542,8 @@
         l = [dist.name for dist in obsoletes_distribution('truffles', '0.9.6')]
         checkLists(l, ['choxie', 'towel-stuff'])
 
-        l = [dist.name for dist in obsoletes_distribution('truffles', '0.5.2.3')]
+        l = [dist.name for dist in obsoletes_distribution('truffles', \
+                                                          '0.5.2.3')]
         checkLists(l, ['choxie', 'towel-stuff'])
 
         l = [dist.name for dist in obsoletes_distribution('truffles', '0.2')]
@@ -372,26 +552,17 @@
 
 def test_suite():
     suite = unittest.TestSuite()
-    testcase_loader = unittest.loader.defaultTestLoader.loadTestsFromTestCase
-    suite.addTest(testcase_loader(TestPkgUtilFunctions))
-    suite.addTest(testcase_loader(TestPkgUtilDistribution))
+    load = unittest.defaultTestLoader.loadTestsFromTestCase
+    suite.addTest(load(TestPkgUtilData))
+    suite.addTest(load(TestPkgUtilDistribution))
+    suite.addTest(load(TestPkgUtilPEP302))
+    suite.addTest(load(TestPkgUtilPEP376))
     return suite
 
+
 def test_main():
     run_unittest(test_suite())
 
-if __name__ == "__main__":
-    test_main()
-
-def test_suite():
-    suite = unittest.TestSuite()
-    testcase_loader = unittest.loader.defaultTestLoader.loadTestsFromTestCase
-    suite.addTest(testcase_loader(TestPkgUtilFunctions))
-    suite.addTest(testcase_loader(TestPkgUtilDistribution))
-    return suite
-
-def test_main():
-    run_unittest(test_suite())
 
 if __name__ == "__main__":
     test_main()
diff --git a/src/distutils2/_backport/tests/test_sysconfig.py b/src/distutils2/_backport/tests/test_sysconfig.py
--- a/src/distutils2/_backport/tests/test_sysconfig.py
+++ b/src/distutils2/_backport/tests/test_sysconfig.py
@@ -8,7 +8,7 @@
 import os
 import shutil
 from copy import copy, deepcopy
-from ConfigParser import ConfigParser
+from ConfigParser import RawConfigParser
 
 from test.test_support import run_unittest, TESTFN
 
@@ -88,15 +88,13 @@
             shutil.rmtree(path)
 
     def test_nested_var_substitution(self):
-        """Assert that the {curly brace token} expansion pattern will replace
-        only the inner {something} on nested expressions like {py{something}} on
-        the first pass.
+        # Assert that the {curly brace token} expansion pattern will replace
+        # only the inner {something} on nested expressions like {py{something}} on
+        # the first pass.
 
-        We have no plans to make use of this, but it keeps the option open for
-        the future, at the cost only of disallowing { itself as a piece of a
-        substitution key (which would be weird).
-
-        """
+        # We have no plans to make use of this, but it keeps the option open for
+        # the future, at the cost only of disallowing { itself as a piece of a
+        # substitution key (which would be weird).
         self.assertEqual(_subst_vars('{py{version}}', {'version': '31'}), '{py31}')
 
     def test_get_paths(self):
@@ -107,7 +105,7 @@
         wanted.sort()
         scheme = scheme.items()
         scheme.sort()
-        self.assertEquals(scheme, wanted)
+        self.assertEqual(scheme, wanted)
 
     def test_get_config_vars(self):
         cvars = get_config_vars()
@@ -120,21 +118,21 @@
         sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) '
                        '[MSC v.1310 32 bit (Intel)]')
         sys.platform = 'win32'
-        self.assertEquals(get_platform(), 'win32')
+        self.assertEqual(get_platform(), 'win32')
 
         # windows XP, amd64
         os.name = 'nt'
         sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) '
                        '[MSC v.1310 32 bit (Amd64)]')
         sys.platform = 'win32'
-        self.assertEquals(get_platform(), 'win-amd64')
+        self.assertEqual(get_platform(), 'win-amd64')
 
         # windows XP, itanium
         os.name = 'nt'
         sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) '
                        '[MSC v.1310 32 bit (Itanium)]')
         sys.platform = 'win32'
-        self.assertEquals(get_platform(), 'win-ia64')
+        self.assertEqual(get_platform(), 'win-ia64')
 
         # macbook
         os.name = 'posix'
@@ -153,9 +151,9 @@
         maxint = sys.maxint
         try:
             sys.maxint = 2147483647
-            self.assertEquals(get_platform(), 'macosx-10.3-ppc')
+            self.assertEqual(get_platform(), 'macosx-10.3-ppc')
             sys.maxint = 9223372036854775807
-            self.assertEquals(get_platform(), 'macosx-10.3-ppc64')
+            self.assertEqual(get_platform(), 'macosx-10.3-ppc64')
         finally:
             sys.maxint = maxint
 
@@ -173,9 +171,9 @@
         maxint = sys.maxint
         try:
             sys.maxint = 2147483647
-            self.assertEquals(get_platform(), 'macosx-10.3-i386')
+            self.assertEqual(get_platform(), 'macosx-10.3-i386')
             sys.maxint = 9223372036854775807
-            self.assertEquals(get_platform(), 'macosx-10.3-x86_64')
+            self.assertEqual(get_platform(), 'macosx-10.3-x86_64')
         finally:
             sys.maxint = maxint
 
@@ -186,33 +184,33 @@
                                        '-fno-strict-aliasing -fno-common '
                                        '-dynamic -DNDEBUG -g -O3')
 
-        self.assertEquals(get_platform(), 'macosx-10.4-fat')
+        self.assertEqual(get_platform(), 'macosx-10.4-fat')
 
         get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot '
                                        '/Developer/SDKs/MacOSX10.4u.sdk  '
                                        '-fno-strict-aliasing -fno-common '
                                        '-dynamic -DNDEBUG -g -O3')
 
-        self.assertEquals(get_platform(), 'macosx-10.4-intel')
+        self.assertEqual(get_platform(), 'macosx-10.4-intel')
 
         get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot '
                                        '/Developer/SDKs/MacOSX10.4u.sdk  '
                                        '-fno-strict-aliasing -fno-common '
                                        '-dynamic -DNDEBUG -g -O3')
-        self.assertEquals(get_platform(), 'macosx-10.4-fat3')
+        self.assertEqual(get_platform(), 'macosx-10.4-fat3')
 
         get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot '
                                        '/Developer/SDKs/MacOSX10.4u.sdk  '
                                        '-fno-strict-aliasing -fno-common '
                                        '-dynamic -DNDEBUG -g -O3')
-        self.assertEquals(get_platform(), 'macosx-10.4-universal')
+        self.assertEqual(get_platform(), 'macosx-10.4-universal')
 
         get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot '
                                        '/Developer/SDKs/MacOSX10.4u.sdk  '
                                        '-fno-strict-aliasing -fno-common '
                                        '-dynamic -DNDEBUG -g -O3')
 
-        self.assertEquals(get_platform(), 'macosx-10.4-fat64')
+        self.assertEqual(get_platform(), 'macosx-10.4-fat64')
 
         for arch in ('ppc', 'i386', 'x86_64', 'ppc64'):
             get_config_vars()['CFLAGS'] = ('-arch %s -isysroot '
@@ -220,7 +218,7 @@
                                            '-fno-strict-aliasing -fno-common '
                                            '-dynamic -DNDEBUG -g -O3'%(arch,))
 
-            self.assertEquals(get_platform(), 'macosx-10.4-%s'%(arch,))
+            self.assertEqual(get_platform(), 'macosx-10.4-%s'%(arch,))
 
         # linux debian sarge
         os.name = 'posix'
@@ -230,7 +228,7 @@
         self._set_uname(('Linux', 'aglae', '2.6.21.1dedibox-r7',
                     '#1 Mon Apr 30 17:25:38 CEST 2007', 'i686'))
 
-        self.assertEquals(get_platform(), 'linux-i686')
+        self.assertEqual(get_platform(), 'linux-i686')
 
         # XXX more platforms to tests here
 
@@ -241,11 +239,11 @@
     def test_get_scheme_names(self):
         wanted = ('nt', 'nt_user', 'os2', 'os2_home', 'posix_home',
                   'posix_prefix', 'posix_user')
-        self.assertEquals(get_scheme_names(), wanted)
+        self.assertEqual(get_scheme_names(), wanted)
 
     def test_expand_globals(self):
 
-        config = ConfigParser()
+        config = RawConfigParser()
         config.add_section('globals')
         config.set('globals', 'foo', 'ok')
         config.add_section('posix')
@@ -254,8 +252,8 @@
 
         _expand_globals(config)
 
-        self.assertEquals(config.get('posix', 'foo'), 'ok')
-        self.assertEquals(config.get('posix', 'more'), '/etc/ok')
+        self.assertEqual(config.get('posix', 'foo'), 'ok')
+        self.assertEqual(config.get('posix', 'more'), '/etc/ok')
 
         # we might not have globals after all
         # extending again (==no more globals section)
diff --git a/src/distutils2/command/bdist.py b/src/distutils2/command/bdist.py
--- a/src/distutils2/command/bdist.py
+++ b/src/distutils2/command/bdist.py
@@ -62,7 +62,7 @@
                       'os2': 'zip'}
 
     # Establish the preferred order (for the --help-formats option).
-    format_commands = ['rpm', 'gztar', 'bztar', 'ztar', 'tar',
+    format_commands = ['gztar', 'bztar', 'ztar', 'tar',
                        'wininst', 'zip', 'msi']
 
     # And the real information.
@@ -96,7 +96,7 @@
 
         # 'bdist_base' -- parent of per-built-distribution-format
         # temporary directories (eg. we'll probably have
-        # "build/bdist.<plat>/dumb", "build/bdist.<plat>/rpm", etc.)
+        # "build/bdist.<plat>/dumb", etc.)
         if self.bdist_base is None:
             build_base = self.get_finalized_command('build').build_base
             self.bdist_base = os.path.join(build_base,
@@ -126,7 +126,7 @@
         # Reinitialize and run each command.
         for i in range(len(self.formats)):
             cmd_name = commands[i]
-            sub_cmd = self.reinitialize_command(cmd_name)
+            sub_cmd = self.get_reinitialized_command(cmd_name)
 
             # passing the owner and group names for tar archiving
             if cmd_name == 'bdist_dumb':
diff --git a/src/distutils2/command/bdist_dumb.py b/src/distutils2/command/bdist_dumb.py
--- a/src/distutils2/command/bdist_dumb.py
+++ b/src/distutils2/command/bdist_dumb.py
@@ -85,7 +85,7 @@
         if not self.skip_build:
             self.run_command('build')
 
-        install = self.reinitialize_command('install', reinit_subcommands=1)
+        install = self.get_reinitialized_command('install', reinit_subcommands=1)
         install.root = self.bdist_dir
         install.skip_build = self.skip_build
         install.warn_dir = 0
@@ -115,8 +115,9 @@
                        % (repr(install.install_base),
                           repr(install.install_platbase)))
             else:
-                archive_root = os.path.join(self.bdist_dir,
-                                   ensure_relative(install.install_base))
+                archive_root = os.path.join(
+                    self.bdist_dir,
+                    self._ensure_relative(install.install_base))
 
         # Make the archive
         filename = self.make_archive(pseudoinstall_root,
@@ -135,3 +136,9 @@
             else:
                 rmtree(self.bdist_dir)
 
+    def _ensure_relative(self, path):
+        # copied from dir_util, deleted
+        drive, path = os.path.splitdrive(path)
+        if path[0:1] == os.sep:
+            path = drive + path[1:]
+        return path
diff --git a/src/distutils2/command/bdist_msi.py b/src/distutils2/command/bdist_msi.py
--- a/src/distutils2/command/bdist_msi.py
+++ b/src/distutils2/command/bdist_msi.py
@@ -177,12 +177,12 @@
         if not self.skip_build:
             self.run_command('build')
 
-        install = self.reinitialize_command('install', reinit_subcommands=1)
+        install = self.get_reinitialized_command('install', reinit_subcommands=1)
         install.prefix = self.bdist_dir
         install.skip_build = self.skip_build
         install.warn_dir = 0
 
-        install_lib = self.reinitialize_command('install_lib')
+        install_lib = self.get_reinitialized_command('install_lib')
         # we do not want to include pyc or pyo files
         install_lib.compile = 0
         install_lib.optimize = 0
diff --git a/src/distutils2/command/bdist_wininst.py b/src/distutils2/command/bdist_wininst.py
--- a/src/distutils2/command/bdist_wininst.py
+++ b/src/distutils2/command/bdist_wininst.py
@@ -8,6 +8,7 @@
 import sys
 import os
 import string
+from shutil import rmtree
 try:
     from sysconfig import get_python_version
 except ImportError:
@@ -126,13 +127,13 @@
         if not self.skip_build:
             self.run_command('build')
 
-        install = self.reinitialize_command('install', reinit_subcommands=1)
+        install = self.get_reinitialized_command('install', reinit_subcommands=1)
         install.root = self.bdist_dir
         install.skip_build = self.skip_build
         install.warn_dir = 0
         install.plat_name = self.plat_name
 
-        install_lib = self.reinitialize_command('install_lib')
+        install_lib = self.get_reinitialized_command('install_lib')
         # we do not want to include pyc or pyo files
         install_lib.compile = 0
         install_lib.optimize = 0
diff --git a/src/distutils2/command/build_ext.py b/src/distutils2/command/build_ext.py
--- a/src/distutils2/command/build_ext.py
+++ b/src/distutils2/command/build_ext.py
@@ -188,7 +188,24 @@
         if self.package is None:
             self.package = self.distribution.ext_package
 
+        # Ensure that the list of extensions is valid, i.e. it is a list of
+        # Extension objects.
         self.extensions = self.distribution.ext_modules
+        if self.extensions:
+            if not isinstance(self.extensions, (list, tuple)):
+                type_name = (self.extensions is None and 'None'
+                            or type(self.extensions).__name__)
+                raise DistutilsSetupError(
+                    "'ext_modules' must be a sequence of Extension instances,"
+                    " not %s" % (type_name,))
+            for i, ext in enumerate(self.extensions):
+                if isinstance(ext, Extension):
+                    continue                # OK! (assume type-checking done
+                                            # by Extension constructor)
+                type_name = (ext is None and 'None' or type(ext).__name__)
+                raise DistutilsSetupError(
+                    "'ext_modules' item %d must be an Extension instance,"
+                    " not %s" % (i, type_name))
 
         # Make sure Python's include directories (for Python.h, pyconfig.h,
         # etc.) are in the include search path.
@@ -396,86 +413,7 @@
         # Now actually compile and link everything.
         self.build_extensions()
 
-    def check_extensions_list(self, extensions):
-        """Ensure that the list of extensions (presumably provided as a
-        command option 'extensions') is valid, i.e. it is a list of
-        Extension objects.  We also support the old-style list of 2-tuples,
-        where the tuples are (ext_name, build_info), which are converted to
-        Extension instances here.
-
-        Raise DistutilsSetupError if the structure is invalid anywhere;
-        just returns otherwise.
-        """
-        if not isinstance(extensions, list):
-            raise DistutilsSetupError, \
-                  "'ext_modules' option must be a list of Extension instances"
-
-        for i, ext in enumerate(extensions):
-            if isinstance(ext, Extension):
-                continue                # OK! (assume type-checking done
-                                        # by Extension constructor)
-
-            if not isinstance(ext, tuple) or len(ext) != 2:
-                raise DistutilsSetupError, \
-                      ("each element of 'ext_modules' option must be an "
-                       "Extension instance or 2-tuple")
-
-            ext_name, build_info = ext
-
-            log.warn(("old-style (ext_name, build_info) tuple found in "
-                      "ext_modules for extension '%s'"
-                      "-- please convert to Extension instance" % ext_name))
-
-            if not (isinstance(ext_name, str) and
-                    extension_name_re.match(ext_name)):
-                raise DistutilsSetupError, \
-                      ("first element of each tuple in 'ext_modules' "
-                       "must be the extension name (a string)")
-
-            if not isinstance(build_info, dict):
-                raise DistutilsSetupError, \
-                      ("second element of each tuple in 'ext_modules' "
-                       "must be a dictionary (build info)")
-
-            # OK, the (ext_name, build_info) dict is type-safe: convert it
-            # to an Extension instance.
-            ext = Extension(ext_name, build_info['sources'])
-
-            # Easy stuff: one-to-one mapping from dict elements to
-            # instance attributes.
-            for key in ('include_dirs', 'library_dirs', 'libraries',
-                        'extra_objects', 'extra_compile_args',
-                        'extra_link_args'):
-                val = build_info.get(key)
-                if val is not None:
-                    setattr(ext, key, val)
-
-            # Medium-easy stuff: same syntax/semantics, different names.
-            ext.runtime_library_dirs = build_info.get('rpath')
-            if 'def_file' in build_info:
-                log.warn("'def_file' element of build info dict "
-                         "no longer supported")
-
-            # Non-trivial stuff: 'macros' split into 'define_macros'
-            # and 'undef_macros'.
-            macros = build_info.get('macros')
-            if macros:
-                ext.define_macros = []
-                ext.undef_macros = []
-                for macro in macros:
-                    if not (isinstance(macro, tuple) and len(macro) in (1, 2)):
-                        raise DistutilsSetupError, \
-                              ("'macros' element of build info dict "
-                               "must be 1- or 2-tuple")
-                    if len(macro) == 1:
-                        ext.undef_macros.append(macro[0])
-                    elif len(macro) == 2:
-                        ext.define_macros.append(macro)
-
-            extensions[i] = ext
-
     def get_source_files(self):
-        self.check_extensions_list(self.extensions)
         filenames = []
 
         # Wouldn't it be neat if we knew the names of header files too...
@@ -485,11 +423,6 @@
         return filenames
 
     def get_outputs(self):
-        # Sanity check the 'extensions' list -- can't assume this is being
-        # done in the same run as a 'build_extensions()' call (in fact, we
-        # can probably assume that it *isn't*!).
-        self.check_extensions_list(self.extensions)
-
         # And build the list of output (built) filenames.  Note that this
         # ignores the 'inplace' flag, and assumes everything goes in the
         # "build" tree.
@@ -499,9 +432,6 @@
         return outputs
 
     def build_extensions(self):
-        # First, sanity-check the 'extensions' list
-        self.check_extensions_list(self.extensions)
-
         for ext in self.extensions:
             try:
                 self.build_extension(ext)
diff --git a/src/distutils2/command/build_py.py b/src/distutils2/command/build_py.py
--- a/src/distutils2/command/build_py.py
+++ b/src/distutils2/command/build_py.py
@@ -6,14 +6,66 @@
 
 import os
 import sys
+import logging
 from glob import glob
 
+import distutils2
 from distutils2.core import Command
 from distutils2.errors import DistutilsOptionError, DistutilsFileError
 from distutils2.util import convert_path
-from distutils2 import log
+from distutils2.converter.refactor import DistutilsRefactoringTool
 
-class build_py(Command):
+# marking public APIs
+__all__ = ['Mixin2to3', 'build_py']
+
+try:
+    from distutils2.util import Mixin2to3 as _Mixin2to3
+    from lib2to3.refactor import get_fixers_from_package
+    _CONVERT = True
+    _KLASS = _Mixin2to3
+except ImportError:
+    _CONVERT = False
+    _KLASS = object
+
+class Mixin2to3(_KLASS):
+    """ The base class which can be used for refactoring. When run under
+    Python 3.0, the run_2to3 method provided by Mixin2to3 is overridden.
+    When run on Python 2.x, it merely creates a class which overrides run_2to3,
+    yet does nothing in particular with it.
+    """
+    if _CONVERT:
+        def _run_2to3(self, files, doctests=[]):
+            """ Takes a list of files and doctests, and performs conversion
+            on those.
+              - First, the files which contain the code(`files`) are converted.
+              - Second, the doctests in `files` are converted.
+              - Thirdly, the doctests in `doctests` are converted.
+            """
+
+            # Convert the ".py" files.
+            logging.info("Converting Python code")
+            _KLASS.run_2to3(self, files)
+
+            # Convert the doctests in the ".py" files.
+            logging.info("Converting doctests with '.py' files")
+            _KLASS.run_2to3(self, files, doctests_only=True)
+
+            # If the following conditions are met, then convert:-
+            # 1. User has specified the 'convert_2to3_doctests' option. So, we
+            #    can expect that the list 'doctests' is not empty.
+            # 2. The default is allow distutils2 to allow conversion of text files
+            #    containing doctests. It is set as
+            #    distutils2.run_2to3_on_doctests
+
+            if doctests != [] and distutils2.run_2to3_on_doctests:
+                logging.info("Converting text files which contain doctests")
+                _KLASS.run_2to3(self, doctests, doctests_only=True)
+    else:
+        # If run on Python 2.x, there is nothing to do.
+        def _run_2to3(self, files, doctests=[]):
+            pass
+
+class build_py(Command, Mixin2to3):
 
     description = "\"build\" pure Python modules (copy to build directory)"
 
@@ -39,6 +91,8 @@
         self.compile = 0
         self.optimize = 0
         self.force = None
+        self._updated_files = []
+        self._doctests_2to3 = []
 
     def finalize_options(self):
         self.set_undefined_options('build',
@@ -93,10 +147,18 @@
             self.build_packages()
             self.build_package_data()
 
+        if self.distribution.use_2to3 and self_updated_files:
+            self.run_2to3(self._updated_files, self._doctests_2to3)
+
         self.byte_compile(self.get_outputs(include_bytecode=0))
 
+    # -- Top-level worker functions ------------------------------------
+
     def get_data_files(self):
-        """Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
+        """Generate list of '(package,src_dir,build_dir,filenames)' tuples.
+
+        Helper function for `finalize_options()`.
+        """
         data = []
         if not self.packages:
             return data
@@ -120,7 +182,10 @@
         return data
 
     def find_data_files(self, package, src_dir):
-        """Return filenames for package's data files in 'src_dir'"""
+        """Return filenames for package's data files in 'src_dir'.
+
+        Helper function for `get_data_files()`.
+        """
         globs = (self.package_data.get('', [])
                  + self.package_data.get(package, []))
         files = []
@@ -132,14 +197,21 @@
         return files
 
     def build_package_data(self):
-        """Copy data files into build directory"""
+        """Copy data files into build directory.
+
+        Helper function for `run()`.
+        """
         for package, src_dir, build_dir, filenames in self.data_files:
             for filename in filenames:
                 target = os.path.join(build_dir, filename)
                 self.mkpath(os.path.dirname(target))
-                self.copy_file(os.path.join(src_dir, filename), target,
-                               preserve_mode=False)
+                outf, copied = self.copy_file(os.path.join(src_dir, filename),
+                               target, preserve_mode=False)
+                if copied and srcfile in self.distribution.convert_2to3.doctests:
+                    self._doctests_2to3.append(outf)
 
+    # XXX - this should be moved to the Distribution class as it is not
+    # only needed for build_py. It also has no dependencies on this class.
     def get_package_dir(self, package):
         """Return the directory, relative to the top of the source
            distribution, where package 'package' should be found
@@ -181,6 +253,8 @@
                     return ''
 
     def check_package(self, package, package_dir):
+        """Helper function for `find_package_modules()` and `find_modules()'.
+        """
         # Empty dir name means current directory, which we can probably
         # assume exists.  Also, os.path.exists and isdir don't know about
         # my "empty string means current dir" convention, so we have to
@@ -200,8 +274,8 @@
             if os.path.isfile(init_py):
                 return init_py
             else:
-                log.warn(("package init file '%s' not found " +
-                          "(or not a regular file)"), init_py)
+                logging.warning(("package init file '%s' not found " +
+                                 "(or not a regular file)"), init_py)
 
         # Either not in a package at all (__init__.py not expected), or
         # __init__.py doesn't exist -- so don't return the filename.
@@ -209,7 +283,8 @@
 
     def check_module(self, module, module_file):
         if not os.path.isfile(module_file):
-            log.warn("file %s (for module %s) not found", module_file, module)
+            logging.warning("file %s (for module %s) not found",
+                            module_file, module)
             return False
         else:
             return True
diff --git a/src/distutils2/command/cmd.py b/src/distutils2/command/cmd.py
--- a/src/distutils2/command/cmd.py
+++ b/src/distutils2/command/cmd.py
@@ -6,7 +6,7 @@
 
 __revision__ = "$Id: cmd.py 75192 2009-10-02 23:49:48Z tarek.ziade $"
 
-import sys, os, re
+import os, re
 from distutils2.errors import DistutilsOptionError
 from distutils2 import util
 from distutils2 import log
@@ -19,7 +19,7 @@
 except ImportError:
     from distutils2._backport.shutil import make_archive
 
-class Command:
+class Command(object):
     """Abstract base class for defining command classes, the "worker bees"
     of the Distutils.  A useful analogy for command classes is to think of
     them as subroutines with local variables called "options".  The options
@@ -188,6 +188,31 @@
         """
         log.log(level, msg)
 
+    # -- External interface --------------------------------------------
+    # (called by outsiders)
+
+    def get_source_files(self):
+        """Return the list of files that are used as inputs to this command,
+        i.e. the files used to generate the output files.  The result is used
+        by the `sdist` command in determining the set of default files.
+
+        Command classes should implement this method if they operate on files
+        from the source tree.
+        """
+        return []
+
+    def get_outputs(self):
+        """Return the list of files that would be produced if this command
+        were actually run.  Not affected by the "dry-run" flag or whether
+        any other commands have been run.
+
+        Command classes should implement this method if they produce any
+        output files that get consumed by another command.  e.g., `build_ext`
+        returns the list of built extension modules, but not any temporary
+        files used in the compilation process.
+        """
+        return []
+
     # -- Option validation methods -------------------------------------
     # (these are very handy in writing the 'finalize_options()' method)
     #
@@ -307,10 +332,8 @@
         cmd_obj.ensure_finalized()
         return cmd_obj
 
-    # XXX rename to 'get_reinitialized_command()'? (should do the
-    # same in dist.py, if so)
-    def reinitialize_command(self, command, reinit_subcommands=0):
-        return self.distribution.reinitialize_command(
+    def get_reinitialized_command(self, command, reinit_subcommands=0):
+        return self.distribution.get_reinitialized_command(
             command, reinit_subcommands)
 
     def run_command(self, command):
@@ -350,8 +373,10 @@
         if os.path.isdir(name) or name == '':
             return
         if dry_run:
+            head = ''
             for part in name.split(os.sep):
-                self.log(part)
+                log.info("created directory %s%s", head, part)
+                head += part + os.sep
             return
         os.makedirs(name, mode)
 
@@ -386,7 +411,7 @@
 
     def spawn(self, cmd, search_path=1, level=1):
         """Spawn an external command respecting dry-run flag."""
-        from distutils2.spawn import spawn
+        from distutils2.util import spawn
         spawn(cmd, search_path, dry_run= self.dry_run)
 
     def make_archive(self, base_name, format, root_dir=None, base_dir=None,
@@ -422,7 +447,7 @@
         # If 'outfile' must be regenerated (either because it doesn't
         # exist, is out-of-date, or the 'force' flag is true) then
         # perform the action that presumably regenerates it
-        if self.force or dep_util.newer_group(infiles, outfile):
+        if self.force or util.newer_group(infiles, outfile):
             self.execute(func, args, exec_msg, level)
 
         # Otherwise, print the "skip" message
diff --git a/src/distutils2/command/register.py b/src/distutils2/command/register.py
--- a/src/distutils2/command/register.py
+++ b/src/distutils2/command/register.py
@@ -28,8 +28,6 @@
     boolean_options = PyPIRCCommand.boolean_options + [
         'verify', 'list-classifiers', 'strict']
 
-    sub_commands = [('check', lambda self: True)]
-
     def initialize_options(self):
         PyPIRCCommand.initialize_options(self)
         self.list_classifiers = 0
@@ -46,9 +44,8 @@
         self.finalize_options()
         self._set_config()
 
-        # Run sub commands
-        for cmd_name in self.get_sub_commands():
-            self.run_command(cmd_name)
+        # Check the package metadata
+        self.run_command('check')
 
         if self.dry_run:
             self.verify_metadata()
diff --git a/src/distutils2/command/sdist.py b/src/distutils2/command/sdist.py
--- a/src/distutils2/command/sdist.py
+++ b/src/distutils2/command/sdist.py
@@ -18,12 +18,11 @@
     from distutils2._backport.shutil import get_archive_formats
 
 from distutils2.core import Command
-from distutils2 import util
 from distutils2.errors import (DistutilsPlatformError, DistutilsOptionError,
-                              DistutilsTemplateError)
+                               DistutilsTemplateError)
 from distutils2.manifest import Manifest
 from distutils2 import log
-from distutils2.util import convert_path, newer
+from distutils2.util import convert_path
 
 def show_formats():
     """Print all possible values for the 'formats' option (used by
@@ -45,12 +44,6 @@
 
     description = "create a source distribution (tarball, zip file, etc.)"
 
-    def checking_metadata(self):
-        """Callable used for the check sub-command.
-
-        Placed here so user_options can view it"""
-        return self.metadata_check
-
     user_options = [
         ('template=', 't',
          "name of manifest template file [default: MANIFEST.in]"),
@@ -100,8 +93,6 @@
     default_format = {'posix': 'gztar',
                       'nt': 'zip' }
 
-    sub_commands = [('check', checking_metadata)]
-
     def initialize_options(self):
         # 'template' and 'manifest' are, respectively, the names of
         # the manifest template and manifest file.
@@ -162,9 +153,9 @@
         # manifest
         self.filelist.clear()
 
-        # Run sub commands
-        for cmd_name in self.get_sub_commands():
-            self.run_command(cmd_name)
+        # Check the package metadata
+        if self.metadata_check:
+            self.run_command('check')
 
         # Do whatever it takes to get the list of files to process
         # (process the manifest template, read an existing manifest,
diff --git a/src/distutils2/command/upload.py b/src/distutils2/command/upload.py
--- a/src/distutils2/command/upload.py
+++ b/src/distutils2/command/upload.py
@@ -15,7 +15,7 @@
 
 from distutils2.errors import DistutilsOptionError
 from distutils2.core import PyPIRCCommand
-from distutils2.spawn import spawn
+from distutils2.util import spawn
 from distutils2 import log
 
 class upload(PyPIRCCommand):
diff --git a/src/distutils2/command/upload_docs.py b/src/distutils2/command/upload_docs.py
--- a/src/distutils2/command/upload_docs.py
+++ b/src/distutils2/command/upload_docs.py
@@ -1,4 +1,4 @@
-import base64, httplib, os.path, socket, tempfile, urlparse, zipfile
+import base64, httplib, os.path, socket, urlparse, zipfile
 from cStringIO import StringIO
 from distutils2 import log
 from distutils2.command.upload import upload
@@ -81,7 +81,6 @@
             raise DistutilsFileError(mesg % upload_dir)
 
     def run(self):
-        tmp_dir = tempfile.mkdtemp()
         name = self.distribution.metadata['Name']
         version = self.distribution.metadata['Version']
         zip_file = zip_dir(self.upload_dir)
@@ -125,7 +124,7 @@
         elif r.status == 301:
             location = r.getheader('Location')
             if location is None:
-                location = 'http://packages.python.org/%s/' % meta.get_name()
+                location = 'http://packages.python.org/%s/' % name
             self.announce('Upload successful. Visit %s' % location,
                           log.INFO)
         else:
diff --git a/src/distutils2/compiler/bcppcompiler.py b/src/distutils2/compiler/bcppcompiler.py
--- a/src/distutils2/compiler/bcppcompiler.py
+++ b/src/distutils2/compiler/bcppcompiler.py
@@ -16,7 +16,7 @@
 import os
 
 from distutils2.errors import (DistutilsExecError, CompileError, LibError,
-                              LinkError, UnknownFileError)
+                               LinkError, UnknownFileError)
 from distutils2.compiler.ccompiler import CCompiler, gen_preprocess_options
 from distutils2.file_util import write_file
 from distutils2.dep_util import newer
diff --git a/src/distutils2/compiler/ccompiler.py b/src/distutils2/compiler/ccompiler.py
--- a/src/distutils2/compiler/ccompiler.py
+++ b/src/distutils2/compiler/ccompiler.py
@@ -10,9 +10,8 @@
 import re
 
 from distutils2.errors import (CompileError, LinkError, UnknownFileError,
-                              DistutilsPlatformError, DistutilsModuleError)
-from distutils2.spawn import spawn
-from distutils2.util import split_quoted, execute, newer_group
+                               DistutilsPlatformError, DistutilsModuleError)
+from distutils2.util import split_quoted, execute, newer_group, spawn
 from distutils2 import log
 from shutil import move
 
@@ -76,7 +75,7 @@
 
         compiler.shared_lib_extension = so_ext
 
-class CCompiler:
+class CCompiler(object):
     """Abstract base class to define the interface that must be implemented
     by real compiler classes.  Also has some utility methods used by
     several compiler classes.
@@ -800,14 +799,16 @@
             library_dirs = []
         fd, fname = tempfile.mkstemp(".c", funcname, text=True)
         f = os.fdopen(fd, "w")
-        for incl in includes:
-            f.write("""#include "%s"\n""" % incl)
-        f.write("""\
+        try:
+            for incl in includes:
+                f.write("""#include "%s"\n""" % incl)
+            f.write("""\
 main (int argc, char **argv) {
     %s();
 }
 """ % funcname)
-        f.close()
+        finally:
+            f.close()
         try:
             objects = self.compile([fname], include_dirs=include_dirs)
         except CompileError:
@@ -938,8 +939,10 @@
         if os.path.isdir(name) or name == '':
             return
         if self.dry_run:
+            head = ''
             for part in name.split(os.sep):
-                self.log(part)
+                log.info("created directory %s%s", head, part)
+                head += part + os.sep
             return
         os.makedirs(name, mode)
 
@@ -1048,7 +1051,7 @@
         module_name = "distutils2.compiler." + module_name
         __import__ (module_name)
         module = sys.modules[module_name]
-        klass = vars(module)[class_name]
+        cls = vars(module)[class_name]
     except ImportError:
         raise DistutilsModuleError, \
               "can't compile C/C++ code: unable to load module '%s'" % \
@@ -1061,7 +1064,7 @@
     # XXX The None is necessary to preserve backwards compatibility
     # with classes that expect verbose to be the first positional
     # argument.
-    return klass(None, dry_run, force)
+    return cls(None, dry_run, force)
 
 
 def gen_preprocess_options(macros, include_dirs):
diff --git a/src/distutils2/compiler/emxccompiler.py b/src/distutils2/compiler/emxccompiler.py
--- a/src/distutils2/compiler/emxccompiler.py
+++ b/src/distutils2/compiler/emxccompiler.py
@@ -25,9 +25,8 @@
 from warnings import warn
 
 from distutils2.compiler.unixccompiler import UnixCCompiler
-from distutils2.util import write_file
 from distutils2.errors import DistutilsExecError, CompileError, UnknownFileError
-from distutils2.util import get_compiler_versions
+from distutils2.util import get_compiler_versions, write_file
 
 class EMXCCompiler (UnixCCompiler):
 
@@ -273,8 +272,10 @@
         # It would probably better to read single lines to search.
         # But we do this only once, and it is fast enough
         f = open(fn)
-        s = f.read()
-        f.close()
+        try:
+            s = f.read()
+        finally:
+            f.close()
 
     except IOError, exc:
         # if we can't read this file, we cannot say it is wrong
diff --git a/src/distutils2/compiler/msvc9compiler.py b/src/distutils2/compiler/msvc9compiler.py
--- a/src/distutils2/compiler/msvc9compiler.py
+++ b/src/distutils2/compiler/msvc9compiler.py
@@ -20,7 +20,7 @@
 import re
 
 from distutils2.errors import (DistutilsExecError, DistutilsPlatformError,
-                              CompileError, LibError, LinkError)
+                               CompileError, LibError, LinkError)
 from distutils2.compiler.ccompiler import CCompiler, gen_lib_options
 from distutils2 import log
 from distutils2.util import get_platform
@@ -50,7 +50,7 @@
     'win-ia64' : 'ia64',
 }
 
-class Reg:
+class Reg(object):
     """Helper class to read values from the registry
     """
 
@@ -112,7 +112,7 @@
         return s
     convert_mbcs = staticmethod(convert_mbcs)
 
-class MacroExpander:
+class MacroExpander(object):
 
     def __init__(self, version):
         self.macros = {}
diff --git a/src/distutils2/compiler/msvccompiler.py b/src/distutils2/compiler/msvccompiler.py
--- a/src/distutils2/compiler/msvccompiler.py
+++ b/src/distutils2/compiler/msvccompiler.py
@@ -15,7 +15,7 @@
 import string
 
 from distutils2.errors import (DistutilsExecError, DistutilsPlatformError,
-                              CompileError, LibError, LinkError)
+                               CompileError, LibError, LinkError)
 from distutils2.compiler.ccompiler import CCompiler, gen_lib_options
 from distutils2 import log
 
@@ -104,7 +104,7 @@
             pass
     return s
 
-class MacroExpander:
+class MacroExpander(object):
 
     def __init__(self, version):
         self.macros = {}
diff --git a/src/distutils2/compiler/unixccompiler.py b/src/distutils2/compiler/unixccompiler.py
--- a/src/distutils2/compiler/unixccompiler.py
+++ b/src/distutils2/compiler/unixccompiler.py
@@ -19,10 +19,10 @@
 from types import StringType, NoneType
 
 from distutils2.util import newer
-from distutils2.compiler.ccompiler import \
-     CCompiler, gen_preprocess_options, gen_lib_options
-from distutils2.errors import \
-     DistutilsExecError, CompileError, LibError, LinkError
+from distutils2.compiler.ccompiler import (CCompiler, gen_preprocess_options,
+                                           gen_lib_options)
+from distutils2.errors import (DistutilsExecError, CompileError,
+                               LibError, LinkError)
 from distutils2 import log
 
 try:
diff --git a/src/distutils2/config.py b/src/distutils2/config.py
--- a/src/distutils2/config.py
+++ b/src/distutils2/config.py
@@ -4,7 +4,7 @@
 that uses .pypirc in the distutils.command package.
 """
 import os
-from ConfigParser import ConfigParser
+from ConfigParser import RawConfigParser
 
 from distutils2.command.cmd import Command
 
@@ -59,7 +59,7 @@
         if os.path.exists(rc):
             self.announce('Using PyPI login from %s' % rc)
             repository = self.repository or self.DEFAULT_REPOSITORY
-            config = ConfigParser()
+            config = RawConfigParser()
             config.read(rc)
             sections = config.sections()
             if 'distutils' in sections:
diff --git a/src/distutils2/converter/fixers/fix_imports.py b/src/distutils2/converter/fixers/fix_imports.py
--- a/src/distutils2/converter/fixers/fix_imports.py
+++ b/src/distutils2/converter/fixers/fix_imports.py
@@ -36,11 +36,16 @@
             pattern = []
             next = imp.next_sibling
             while next is not None:
+                # Get the first child if we have a Node
+                if not hasattr(next, "value"):
+                    next = next.children[0]
                 pattern.append(next.value)
                 if not hasattr(next, "next_sibling"):
                     next.next_sibling = next.get_next_sibling()
                 next = next.next_sibling
-            if pattern == ['import', 'setup']:
+            
+            if set(pattern).issubset(set(
+                    ['import', ',', 'setup', 'find_packages'])):
                 imp.value = 'distutils2.core'
                 imp.changed()
 
diff --git a/src/distutils2/core.py b/src/distutils2/core.py
--- a/src/distutils2/core.py
+++ b/src/distutils2/core.py
@@ -2,8 +2,9 @@
 
 The only module that needs to be imported to use the Distutils; provides
 the 'setup' function (which is to be called from the setup script).  Also
-indirectly provides the Distribution and Command classes, although they are
-really defined in distutils2.dist and distutils2.cmd.
+exports useful classes so that setup scripts can import them from here
+although they are really defined in other modules: Distribution, Command,
+PyPIRCommand, Extension, find_packages.
 """
 
 __revision__ = "$Id: core.py 77704 2010-01-23 09:23:15Z tarek.ziade $"
@@ -12,7 +13,7 @@
 import os
 
 from distutils2.errors import (DistutilsSetupError, DistutilsArgError,
-                              DistutilsError, CCompilerError)
+                               DistutilsError, CCompilerError)
 from distutils2.util import grok_environment_error
 
 # Mainly import these so setup scripts can "from distutils2.core import" them.
@@ -20,6 +21,7 @@
 from distutils2.command.cmd import Command
 from distutils2.config import PyPIRCCommand
 from distutils2.extension import Extension
+from distutils2.util import find_packages
 
 # This is a barebones help message generated displayed when the user
 # runs the setup script with no arguments at all.  More useful help
@@ -47,7 +49,8 @@
                   'maintainer', 'maintainer_email', 'url', 'license',
                   'description', 'long_description', 'keywords',
                   'platforms', 'classifiers', 'download_url',
-                  'requires', 'provides', 'obsoletes',
+                  'requires', 'provides', 'obsoletes', 'use_2to3',
+                  'convert_2to3_doctests',
                   )
 
 # Legal keyword arguments for the Extension constructor
@@ -94,11 +97,7 @@
 
     # Determine the distribution class -- either caller-supplied or
     # our Distribution (see below).
-    klass = attrs.get('distclass')
-    if klass:
-        del attrs['distclass']
-    else:
-        klass = Distribution
+    distclass = attrs.pop('distclass', Distribution)
 
     if 'script_name' not in attrs:
         attrs['script_name'] = os.path.basename(sys.argv[0])
@@ -108,7 +107,7 @@
     # Create the Distribution instance, using the remaining arguments
     # (ie. everything except distclass) to initialize it
     try:
-        _setup_distribution = dist = klass(attrs)
+        _setup_distribution = dist = distclass(attrs)
     except DistutilsSetupError, msg:
         if 'name' in attrs:
             raise SystemExit, "error in %s setup command: %s" % \
diff --git a/src/distutils2/depgraph.py b/src/distutils2/depgraph.py
--- a/src/distutils2/depgraph.py
+++ b/src/distutils2/depgraph.py
@@ -1,47 +1,49 @@
-"""
-A dependency graph generator. The graph is represented as an instance of
-:class:`DependencyGraph`, and DOT output is possible as well.
+"""Analyse the relationships between the distributions in the system and generate
+a dependency graph.
 """
 
-from distutils2._backport import pkgutil
 from distutils2.errors import DistutilsError
 from distutils2.version import VersionPredicate
 
-__all__ = ['DependencyGraph', 'generate_graph']
+__all__ = ['DependencyGraph', 'generate_graph', 'dependent_dists',
+           'graph_to_dot']
 
 
 class DependencyGraph(object):
     """
     Represents a dependency graph between distributions.
 
-    The depedency relationships are stored in an *adjacency_list* that maps
+    The dependency relationships are stored in an ``adjacency_list`` that maps
     distributions to a list of ``(other, label)`` tuples where  ``other``
     is a distribution and the edge is labelled with ``label`` (i.e. the version
-    specifier, if such was provided). If any missing depencies are found,
-    they are stored in ``missing``. It maps distributions to a list of
-    requirements that were not provided by any other distributions.
+    specifier, if such was provided). Also, for more efficient traversal, for
+    every distribution ``x``, a list of predecessors is kept in
+    ``reverse_list[x]``. An edge from distribution ``a`` to
+    distribution ``b`` means that ``a`` depends on ``b``. If any missing
+    depencies are found, they are stored in ``missing``, which is a dictionary
+    that maps distributions to a list of requirements that were not provided by
+    any other distributions.
     """
 
     def __init__(self):
         self.adjacency_list = {}
+        self.reverse_list = {}
         self.missing = {}
 
     def add_distribution(self, distribution):
-        """
-        Add distribution *x* to the graph.
+        """Add the *distribution* to the graph.
 
         :type distribution: :class:`pkgutil.Distribution` or
                             :class:`pkgutil.EggInfoDistribution`
         """
         self.adjacency_list[distribution] = list()
+        self.reverse_list[distribution] = list()
         self.missing[distribution] = list()
 
     def add_edge(self, x, y, label=None):
-        """
-        Add an edge from distribution *x* to distribution *y* with the given
+        """Add an edge from distribution *x* to distribution *y* with the given
         *label*.
 
-
         :type x: :class:`pkgutil.Distribution` or
                  :class:`pkgutil.EggInfoDistribution`
         :type y: :class:`pkgutil.Distribution` or
@@ -49,6 +51,9 @@
         :type label: ``str`` or ``None``
         """
         self.adjacency_list[x].append((y, label))
+        # multiple edges are allowed, so be careful
+        if not x in self.reverse_list[y]:
+            self.reverse_list[y].append(x)
 
     def add_missing(self, distribution, requirement):
         """
@@ -60,45 +65,42 @@
         """
         self.missing[distribution].append(requirement)
 
-    def to_dot(self, f, skip_disconnected=True):
-        """
-        Writes a DOT output for the graph to the provided *file*.
-        If *skip_disconnected* is set to ``True``, then all distributions
-        that are not dependent on any other distributions are skipped.
 
-        :type f: ``file``
-        ;type skip_disconnected: ``bool``
-        """
-        if not isinstance(f, file):
-            raise TypeError('the argument has to be of type file')
+def graph_to_dot(graph, f, skip_disconnected=True):
+    """Writes a DOT output for the graph to the provided file *f*.
 
-        disconnected = []
+    If *skip_disconnected* is set to ``True``, then all distributions
+    that are not dependent on any other distribution are skipped.
 
-        f.write("digraph dependencies {\n")
-        for dist, adjs in self.adjacency_list.iteritems():
-            if len(adjs) == 0 and not skip_disconnected:
-                disconnected.append(dist)
-            for (other, label) in adjs:
-                if not label is None:
-                    f.write('"%s" -> "%s" [label="%s"]\n' %
-                                                (dist.name, other.name, label))
-                else:
-                    f.write('"%s" -> "%s"\n' % (dist.name, other.name))
-        if not skip_disconnected and len(disconnected) > 0:
-            f.write('subgraph disconnected {\n')
-            f.write('label = "Disconnected"\n')
-            f.write('bgcolor = red\n')
+    :type f: has to support ``file``-like operations
+    :type skip_disconnected: ``bool``
+    """
+    disconnected = []
 
-            for dist in disconnected:
-                f.write('"%s"' % dist.name)
-                f.write('\n')
-            f.write('}\n')
+    f.write("digraph dependencies {\n")
+    for dist, adjs in graph.adjacency_list.iteritems():
+        if len(adjs) == 0 and not skip_disconnected:
+            disconnected.append(dist)
+        for (other, label) in adjs:
+            if not label is None:
+                f.write('"%s" -> "%s" [label="%s"]\n' %
+                                            (dist.name, other.name, label))
+            else:
+                f.write('"%s" -> "%s"\n' % (dist.name, other.name))
+    if not skip_disconnected and len(disconnected) > 0:
+        f.write('subgraph disconnected {\n')
+        f.write('label = "Disconnected"\n')
+        f.write('bgcolor = red\n')
+
+        for dist in disconnected:
+            f.write('"%s"' % dist.name)
+            f.write('\n')
         f.write('}\n')
+    f.write('}\n')
 
 
 def generate_graph(dists):
-    """
-    Generates a dependency graph from the given distributions.
+    """Generates a dependency graph from the given distributions.
 
     :parameter dists: a list of distributions
     :type dists: list of :class:`pkgutil.Distribution` and
@@ -115,7 +117,7 @@
         provides = dist.metadata['Provides-Dist'] + dist.metadata['Provides']
 
         for p in provides:
-            comps = p.split(" ", 1)
+            comps = p.strip().rsplit(" ", 1)
             name = comps[0]
             version = None
             if len(comps) == 2:
@@ -133,22 +135,26 @@
         requires = dist.metadata['Requires-Dist'] + dist.metadata['Requires']
         for req in requires:
             predicate = VersionPredicate(req)
-            comps = req.split(" ", 1)
+            comps = req.strip().rsplit(" ", 1)
             name = comps[0]
 
             if not name in provided:
                 graph.add_missing(dist, req)
             else:
+                matched = False
                 for (version, provider) in provided[name]:
                     if predicate.match(version):
                         graph.add_edge(dist, provider, req)
+                        matched = True
+                        break
+                if not matched:
+                    graph.add_missing(dist, req)
 
     return graph
 
 
 def dependent_dists(dists, dist):
-    """
-    Recursively generate a list of distributions from *dists* that are
+    """Recursively generate a list of distributions from *dists* that are
     dependent on *dist*.
 
     :param dists: a list of distributions
@@ -158,24 +164,26 @@
         raise ValueError('The given distribution is not a member of the list')
     graph = generate_graph(dists)
 
-    dep = [dist]
-    fringe = [dist] # list of nodes we should expand
+    dep = [dist] # dependent distributions
+    fringe = graph.reverse_list[dist] # list of nodes we should inspect
+
     while not len(fringe) == 0:
-        next = graph.adjacency_list[fringe.pop()]
-        for (dist, label) in next:
-            if not dist in dep: # avoid infinite loops
-                dep.append(dist)
-                fringe.append(dist)
+        node = fringe.pop()
+        dep.append(node)
+        for prev in graph.reverse_list[node]:
+            if not prev in dep:
+                fringe.append(prev)
 
-    dep.pop()
+    dep.pop(0) # remove dist from dep, was there to prevent infinite loops
     return dep
 
 if __name__ == '__main__':
-    dists = list(pkgutil.get_distributions(use_egg_info=True))
+    from distutils2._backport.pkgutil import get_distributions
+    dists = list(get_distributions(use_egg_info=True))
     graph = generate_graph(dists)
     for dist, reqs in graph.missing.iteritems():
         if len(reqs) > 0:
             print("Missing dependencies for %s: %s" % (dist.name,
                                                        ", ".join(reqs)))
     f = open('output.dot', 'w')
-    graph.to_dot(f, True)
+    graph_to_dot(graph, f, True)
diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py
--- a/src/distutils2/dist.py
+++ b/src/distutils2/dist.py
@@ -13,8 +13,10 @@
 except ImportError:
     warnings = None
 
+from ConfigParser import RawConfigParser
+
 from distutils2.errors import (DistutilsOptionError, DistutilsArgError,
-                              DistutilsModuleError, DistutilsClassError)
+                               DistutilsModuleError, DistutilsClassError)
 from distutils2.fancy_getopt import FancyGetopt, translate_longopt
 from distutils2.util import check_environ, strtobool
 from distutils2 import log
@@ -112,7 +114,11 @@
         ('tests-require', None,
          "print the list of packages/modules required to run the test suite"),
         ('obsoletes', None,
-         "print the list of packages/modules made obsolete")
+         "print the list of packages/modules made obsolete"),
+        ('use-2to3', None,
+         "use 2to3 to make source python 3.x compatible"),
+        ('convert-2to3-doctests', None,
+         "use 2to3 to convert doctests in seperate text files"), 
         ]
     display_option_names = map(lambda x: translate_longopt(x[0]),
                                display_options)
@@ -206,6 +212,8 @@
         self.scripts = None
         self.data_files = None
         self.password = ''
+        self.use_2to3 = False
+        self.convert_2to3_doctests = []
 
         # And now initialize bookkeeping stuff that can't be supplied by
         # the caller at all.  'command_obj' maps command names to
@@ -250,7 +258,7 @@
                 elif hasattr(self, key):
                     setattr(self, key, val)
                 else:
-                    msg = "Unknown distribution option: %s" % repr(key)
+                    msg = "Unknown distribution option: %r" % key
                     if warnings is not None:
                         warnings.warn(msg)
                     else:
@@ -364,14 +372,12 @@
         return files
 
     def parse_config_files(self, filenames=None):
-        from ConfigParser import ConfigParser
-
         if filenames is None:
             filenames = self.find_config_files()
 
         log.debug("Distribution.parse_config_files():")
 
-        parser = ConfigParser()
+        parser = RawConfigParser()
         for filename in filenames:
             log.debug("  reading %s" % filename)
             parser.read(filename)
@@ -385,7 +391,7 @@
                         opt = opt.replace('-', '_')
                         opt_dict[opt] = (filename, val)
 
-            # Make the ConfigParser forget everything (so we retain
+            # Make the RawConfigParser forget everything (so we retain
             # the original filenames that options come from)
             parser.__init__()
 
@@ -583,15 +589,11 @@
         instance, analogous to the .finalize_options() method of Command
         objects.
         """
-
-        # XXX conversion -- removed
-        #for attr in ('keywords', 'platforms'):
-        #    value = self.metadata.get_field(attr)
-        #    if value is None:
-        #        continue
-        #    if isinstance(value, str):
-        #        value = [elm.strip() for elm in value.split(',')]
-        #        setattr(self.metadata, attr, value)
+        if getattr(self, 'convert_2to3_doctests', None):
+            self.convert_2to3_doctests = [os.path.join(p) 
+                                for p in self.convert_2to3_doctests]
+        else:
+            self.convert_2to3_doctests = []
 
     def _show_help(self, parser, global_options=1, display_options=1,
                    commands=[]):
@@ -629,16 +631,16 @@
 
         for command in self.commands:
             if isinstance(command, type) and issubclass(command, Command):
-                klass = command
+                cls = command
             else:
-                klass = self.get_command_class(command)
-            if (hasattr(klass, 'help_options') and
-                isinstance(klass.help_options, list)):
-                parser.set_option_table(klass.user_options +
-                                        fix_help_options(klass.help_options))
+                cls = self.get_command_class(command)
+            if (hasattr(cls, 'help_options') and
+                isinstance(cls.help_options, list)):
+                parser.set_option_table(cls.user_options +
+                                        fix_help_options(cls.help_options))
             else:
-                parser.set_option_table(klass.user_options)
-            parser.print_help("Options for '%s' command:" % klass.__name__)
+                parser.set_option_table(cls.user_options)
+            parser.print_help("Options for '%s' command:" % cls.__name__)
             print('')
 
         print(gen_usage(self.script_name))
@@ -690,11 +692,11 @@
         print(header + ":")
 
         for cmd in commands:
-            klass = self.cmdclass.get(cmd)
-            if not klass:
-                klass = self.get_command_class(cmd)
+            cls = self.cmdclass.get(cmd)
+            if not cls:
+                cls = self.get_command_class(cmd)
             try:
-                description = klass.description
+                description = cls.description
             except AttributeError:
                 description = "(no description available)"
 
@@ -756,11 +758,11 @@
 
         rv = []
         for cmd in (std_commands + extra_commands):
-            klass = self.cmdclass.get(cmd)
-            if not klass:
-                klass = self.get_command_class(cmd)
+            cls = self.cmdclass.get(cmd)
+            if not cls:
+                cls = self.get_command_class(cmd)
             try:
-                description = klass.description
+                description = cls.description
             except AttributeError:
                 description = "(no description available)"
             rv.append((cmd, description))
@@ -792,13 +794,13 @@
         Raises DistutilsModuleError if the expected module could not be
         found, or if that module does not define the expected class.
         """
-        klass = self.cmdclass.get(command)
-        if klass:
-            return klass
+        cls = self.cmdclass.get(command)
+        if cls:
+            return cls
 
         for pkgname in self.get_command_packages():
             module_name = "%s.%s" % (pkgname, command)
-            klass_name = command
+            class_name = command
 
             try:
                 __import__ (module_name)
@@ -807,14 +809,14 @@
                 continue
 
             try:
-                klass = getattr(module, klass_name)
+                cls = getattr(module, class_name)
             except AttributeError:
                 raise DistutilsModuleError, \
                       "invalid command '%s' (no class '%s' in module '%s')" \
-                      % (command, klass_name, module_name)
+                      % (command, class_name, module_name)
 
-            self.cmdclass[command] = klass
-            return klass
+            self.cmdclass[command] = cls
+            return cls
 
         raise DistutilsModuleError("invalid command '%s'" % command)
 
@@ -830,8 +832,8 @@
             log.debug("Distribution.get_command_obj(): " \
                       "creating '%s' command object" % command)
 
-            klass = self.get_command_class(command)
-            cmd_obj = self.command_obj[command] = klass(self)
+            cls = self.get_command_class(command)
+            cmd_obj = self.command_obj[command] = cls(self)
             self.have_run[command] = 0
 
             # Set any options that were supplied in config files
@@ -887,7 +889,7 @@
             except ValueError, msg:
                 raise DistutilsOptionError, msg
 
-    def reinitialize_command(self, command, reinit_subcommands=0):
+    def get_reinitialized_command(self, command, reinit_subcommands=0):
         """Reinitializes a command to the state it was in when first
         returned by 'get_command_obj()': ie., initialized but not yet
         finalized.  This provides the opportunity to sneak option
@@ -922,7 +924,7 @@
 
         if reinit_subcommands:
             for sub in command.get_sub_commands():
-                self.reinitialize_command(sub, reinit_subcommands)
+                self.get_reinitialized_command(sub, reinit_subcommands)
 
         return command
 
diff --git a/src/distutils2/extension.py b/src/distutils2/extension.py
--- a/src/distutils2/extension.py
+++ b/src/distutils2/extension.py
@@ -5,14 +5,8 @@
 
 __revision__ = "$Id: extension.py 77704 2010-01-23 09:23:15Z tarek.ziade $"
 
-import os
 import warnings
 
-try:
-    import sysconfig
-except ImportError:
-    from distutils2._backport import sysconfig
-
 # This class is really only used by the "build_ext" command, so it might
 # make sense to put it in distutils.command.build_ext.  However, that
 # module is already big enough, and I want to make this class a bit more
@@ -23,7 +17,7 @@
 # import that large-ish module (indirectly, through distutils.core) in
 # order to do anything.
 
-class Extension:
+class Extension(object):
     """Just a collection of attributes that describes an extension
     module and everything needed to build it (hopefully in a portable
     way, but there are hooks that let you be as unportable as you need).
diff --git a/src/distutils2/fancy_getopt.py b/src/distutils2/fancy_getopt.py
--- a/src/distutils2/fancy_getopt.py
+++ b/src/distutils2/fancy_getopt.py
@@ -30,7 +30,7 @@
 # (for use as attributes of some object).
 longopt_xlate = string.maketrans('-', '_')
 
-class FancyGetopt:
+class FancyGetopt(object):
     """Wrapper around the standard 'getopt()' module that provides some
     handy extra functionality:
       * short and long options are tied together
@@ -473,7 +473,7 @@
     return string.translate(opt, longopt_xlate)
 
 
-class OptionDummy:
+class OptionDummy(object):
     """Dummy class just used as a place to hold command-line option
     values as instance attributes."""
 
diff --git a/src/distutils2/log.py b/src/distutils2/log.py
--- a/src/distutils2/log.py
+++ b/src/distutils2/log.py
@@ -11,14 +11,14 @@
 
 import sys
 
-class Log:
+class Log(object):
 
     def __init__(self, threshold=WARN):
         self.threshold = threshold
 
     def _log(self, level, msg, args):
         if level not in (DEBUG, INFO, WARN, ERROR, FATAL):
-            raise ValueError('%s wrong log level' % str(level))
+            raise ValueError('%s wrong log level' % level)
 
         if level >= self.threshold:
             if args:
diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py
--- a/src/distutils2/metadata.py
+++ b/src/distutils2/metadata.py
@@ -105,7 +105,6 @@
     keys = fields.keys()
     possible_versions = ['1.0', '1.1', '1.2']
 
-
     # first let's try to see if a field is not part of one of the version
     for key in keys:
         if key not in _241_FIELDS and '1.0' in possible_versions:
@@ -128,9 +127,9 @@
         raise MetadataConflictError('You used incompatible 1.1 and 1.2 fields')
 
     # we have the choice, either 1.0, or 1.2
-    #   - 1.0 has a broken Summary field but work with all tools
+    #   - 1.0 has a broken Summary field but works with all tools
     #   - 1.1 is to avoid
-    #   - 1.2 fixes Summary but is not spreaded yet
+    #   - 1.2 fixes Summary but is not widespread yet
     if not is_1_1 and not is_1_2:
         # we couldn't find any specific marker
         if PKG_INFO_PREFERRED_VERSION in possible_versions:
@@ -185,14 +184,16 @@
 class DistributionMetadata(object):
     """Distribution meta-data class (1.0 or 1.2).
     """
-    def __init__(self, path=None, platform_dependant=False,
-                 execution_context=None):
+    def __init__(self, path=None, platform_dependent=False,
+                 execution_context=None, fileobj=None):
         self._fields = {}
         self.version = None
         self.docutils_support = _HAS_DOCUTILS
-        self.platform_dependant = platform_dependant
+        self.platform_dependent = platform_dependent
         if path is not None:
             self.read(path)
+        elif fileobj is not None:
+            self.read_file(fileobj)
         self.execution_context = execution_context
 
     def _set_best_version(self):
@@ -261,7 +262,7 @@
         return reporter.messages
 
     def _platform(self, value):
-        if not self.platform_dependant or ';' not in value:
+        if not self.platform_dependent or ';' not in value:
             return True, value
         value, marker = value.split(';')
         return _interpret(marker, self.execution_context), value
@@ -516,7 +517,7 @@
             raise NameError(value)
 
     def _nonsense_op(self):
-        msg = 'This operation is not supported : "%s"' % str(self)
+        msg = 'This operation is not supported : "%s"' % self
         raise SyntaxError(msg)
 
     def __call__(self):
@@ -633,4 +634,3 @@
     operations = _CHAIN(execution_context)
     tokenize(StringIO(marker).readline, operations.eat)
     return operations.result()
-
diff --git a/src/distutils2/mkpkg.py b/src/distutils2/mkpkg.py
--- a/src/distutils2/mkpkg.py
+++ b/src/distutils2/mkpkg.py
@@ -675,7 +675,7 @@
 troveDict = buildTroveDict(troveList)
 
 
-class SetupClass:
+class SetupClass(object):
     def __init__(self):
         self.config = None
         self.classifierDict = {}
@@ -717,14 +717,16 @@
 
     def inspectFile(self, path):
         fp = open(path, 'r')
-        for line in [ fp.readline() for x in range(10) ]:
-            m = re.match(r'^#!.*python((?P<major>\d)(\.\d+)?)?$', line)
-            if m:
-                if m.group('major') == '3':
-                    self.classifierDict['Programming Language :: Python :: 3'] = 1
-                else:
-                    self.classifierDict['Programming Language :: Python :: 2'] = 1
-        fp.close()
+        try:
+            for line in [ fp.readline() for x in range(10) ]:
+                m = re.match(r'^#!.*python((?P<major>\d)(\.\d+)?)?$', line)
+                if m:
+                    if m.group('major') == '3':
+                        self.classifierDict['Programming Language :: Python :: 3'] = 1
+                    else:
+                        self.classifierDict['Programming Language :: Python :: 2'] = 1
+        finally:
+            fp.close()
 
 
     def inspectDirectory(self):
@@ -885,38 +887,33 @@
         if os.path.exists('setup.py'): shutil.move('setup.py', 'setup.py.old')
 
         fp = open('setup.py', 'w')
-        fp.write('#!/usr/bin/env python\n\n')
-        fp.write('from distutils2.core import setup\n\n')
+        try:
+            fp.write('#!/usr/bin/env python\n\n')
+            fp.write('from distutils2.core import setup\n\n')
+            fp.write('setup(name=%s,\n' % repr(self.setupData['name']))
+            fp.write('      version=%s,\n' % repr(self.setupData['version']))
+            fp.write('      description=%s,\n'
+                    % repr(self.setupData['description']))
+            fp.write('      author=%s,\n' % repr(self.setupData['author']))
+            fp.write('      author_email=%s,\n'
+                    % repr(self.setupData['author_email']))
+            if self.setupData['url']:
+                fp.write('      url=%s,\n' % repr(self.setupData['url']))
+            if self.setupData['classifier']:
+                fp.write('      classifier=[\n')
+                for classifier in sorted(self.setupData['classifier'].keys()):
+                    fp.write('            %s,\n' % repr(classifier))
+                fp.write('         ],\n')
+            if self.setupData['packages']:
+                fp.write('      packages=%s,\n'
+                        % repr(self._dotted_packages(self.setupData['packages'])))
+                fp.write('      package_dir=%s,\n'
+                        % repr(self.setupData['packages']))
+            fp.write('      #scripts=[\'path/to/script\']\n')
 
-        fp.write('from sys import version\n')
-        fp.write('if version < \'2.2.3\':\n')
-        fp.write('    from distutils2.dist import DistributionMetadata\n')
-        fp.write('    DistributionMetadata.classifier = None\n')
-        fp.write('    DistributionMetadata.download_url = None\n')
-
-        fp.write('setup(name = %s,\n' % repr(self.setupData['name']))
-        fp.write('        version = %s,\n' % repr(self.setupData['version']))
-        fp.write('        description = %s,\n'
-                % repr(self.setupData['description']))
-        fp.write('        author = %s,\n' % repr(self.setupData['author']))
-        fp.write('        author_email = %s,\n'
-                % repr(self.setupData['author_email']))
-        if self.setupData['url']:
-            fp.write('        url = %s,\n' % repr(self.setupData['url']))
-        if self.setupData['classifier']:
-            fp.write('        classifier = [\n')
-            for classifier in sorted(self.setupData['classifier'].keys()):
-                fp.write('              %s,\n' % repr(classifier))
-            fp.write('           ],\n')
-        if self.setupData['packages']:
-            fp.write('        packages = %s,\n'
-                    % repr(self._dotted_packages(self.setupData['packages'])))
-            fp.write('        package_dir = %s,\n'
-                    % repr(self.setupData['packages']))
-        fp.write('        #scripts = [\'path/to/script\']\n')
-
-        fp.write('        )\n')
-        fp.close()
+            fp.write('      )\n')
+        finally:
+            fp.close()
         os.chmod('setup.py', 0755)
 
         print 'Wrote "setup.py".'
diff --git a/src/distutils2/pypi/__init__.py b/src/distutils2/pypi/__init__.py
--- a/src/distutils2/pypi/__init__.py
+++ b/src/distutils2/pypi/__init__.py
@@ -3,5 +3,6 @@
 Package containing ways to interact with the PyPI APIs.
 """ 
 
-__all__ = ['package_index',
+__all__ = ['simple',
+           'dist',
 ]
diff --git a/src/distutils2/pypi/dist.py b/src/distutils2/pypi/dist.py
new file mode 100644
--- /dev/null
+++ b/src/distutils2/pypi/dist.py
@@ -0,0 +1,315 @@
+"""distutils2.pypi.dist
+
+Provides the PyPIDistribution class thats represents a distribution retrieved
+on PyPI.
+"""
+import re
+import urlparse
+import urllib
+import tempfile
+from operator import attrgetter
+
+try:
+    import hashlib
+except ImportError:
+    from distutils2._backport import hashlib
+
+from distutils2.version import suggest_normalized_version, NormalizedVersion
+from distutils2.pypi.errors import HashDoesNotMatch, UnsupportedHashName
+
+EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz .egg".split()
+MD5_HASH = re.compile(r'^.*#md5=([a-f0-9]+)$')
+
+
+class PyPIDistribution(object):
+    """Represents a distribution retrieved from PyPI.
+
+    This is a simple container for various attributes as name, version,
+    downloaded_location, url etc.
+
+    The PyPIDistribution class is used by the pypi.*Index class to return
+    information about distributions.
+    """
+
+    @classmethod
+    def from_url(cls, url, probable_dist_name=None, is_external=True):
+        """Build a Distribution from a url archive (egg or zip or tgz).
+
+        :param url: complete url of the distribution
+        :param probable_dist_name: A probable name of the distribution.
+        :param is_external: Tell if the url commes from an index or from
+                            an external URL.
+        """
+        # if the url contains a md5 hash, get it.
+        md5_hash = None
+        match = MD5_HASH.match(url)
+        if match is not None:
+            md5_hash = match.group(1)
+            # remove the hash
+            url = url.replace("#md5=%s" % md5_hash, "")
+
+        # parse the archive name to find dist name and version
+        archive_name = urlparse.urlparse(url)[2].split('/')[-1]
+        extension_matched = False
+        # remove the extension from the name
+        for ext in EXTENSIONS:
+            if archive_name.endswith(ext):
+                archive_name = archive_name[:-len(ext)]
+                extension_matched = True
+
+        name, version = split_archive_name(archive_name)
+        if extension_matched is True:
+            return PyPIDistribution(name, version, url=url, url_hashname="md5",
+                                    url_hashval=md5_hash,
+                                    url_is_external=is_external)
+
+    def __init__(self, name, version, type=None, url=None, url_hashname=None,
+                 url_hashval=None, url_is_external=True):
+        """Create a new instance of PyPIDistribution.
+
+        :param name: the name of the distribution
+        :param version: the version of the distribution
+        :param type: the type of the dist (eg. source, bin-*, etc.)
+        :param url: URL where we found this distribution
+        :param url_hashname: the name of the hash we want to use. Refer to the
+                         hashlib.new documentation for more information.
+        :param url_hashval: the hash value.
+        :param url_is_external: we need to know if the provided url comes from an
+                            index browsing, or from an external resource.
+
+        """
+        self.name = name
+        self.version = NormalizedVersion(version)
+        self.type = type
+        # set the downloaded path to None by default. The goal here
+        # is to not download distributions multiple times
+        self.downloaded_location = None
+        # We store urls in dict, because we need to have a bit more informations
+        # than the simple URL. It will be used later to find the good url to
+        # use.
+        # We have two _url* attributes: _url and _urls. _urls contains a list of
+        # dict for the different urls, and _url contains the choosen url, in
+        # order to dont make the selection process multiple times.
+        self._urls = []
+        self._url = None
+        self.add_url(url, url_hashname, url_hashval, url_is_external)
+
+    def add_url(self, url, hashname=None, hashval=None, is_external=True):
+        """Add a new url to the list of urls"""
+        if hashname is not None:
+            try:
+                hashlib.new(hashname)
+            except ValueError:
+                raise UnsupportedHashName(hashname)
+
+        self._urls.append({
+            'url': url,
+            'hashname': hashname,
+            'hashval': hashval,
+            'is_external': is_external,
+        })
+        # reset the url selection process
+        self._url = None
+
+    @property
+    def url(self):
+        """Pick up the right url for the list of urls in self.urls"""
+        # We return internal urls over externals.
+        # If there is more than one internal or external, return the first
+        # one.
+        if self._url is None:
+            if len(self._urls) > 1:
+                internals_urls = [u for u in self._urls \
+                                  if u['is_external'] == False]
+                if len(internals_urls) >= 1:
+                    self._url = internals_urls[0]
+            if self._url is None:
+                self._url = self._urls[0]
+        return self._url
+
+    @property
+    def is_source(self):
+        """return if the distribution is a source one or not"""
+        return self.type == 'source'
+
+    @property
+    def is_final(self):
+        """proxy to version.is_final"""
+        return self.version.is_final
+
+    def download(self, path=None):
+        """Download the distribution to a path, and return it.
+
+        If the path is given in path, use this, otherwise, generates a new one
+        """
+        if path is None:
+            path = tempfile.mkdtemp()
+
+        # if we do not have downloaded it yet, do it.
+        if self.downloaded_location is None:
+            url = self.url['url']
+            archive_name = urlparse.urlparse(url)[2].split('/')[-1]
+            filename, headers = urllib.urlretrieve(url,
+                                                   path + "/" + archive_name)
+            self.downloaded_location = filename
+            self._check_md5(filename)
+        return self.downloaded_location
+
+    def _check_md5(self, filename):
+        """Check that the md5 checksum of the given file matches the one in
+        url param"""
+        hashname = self.url['hashname']
+        expected_hashval = self.url['hashval']
+        if not None in (expected_hashval, hashname):
+            f = open(filename)
+            hashval = hashlib.new(hashname)
+            hashval.update(f.read())
+            if hashval.hexdigest() != expected_hashval:
+                raise HashDoesNotMatch("got %s instead of %s"
+                    % (hashval.hexdigest(), expected_hashval))
+
+    def __repr__(self):
+        return "%s %s %s %s" \
+            % (self.__class__.__name__, self.name, self.version,
+               self.type or "")
+
+    def _check_is_comparable(self, other):
+        if not isinstance(other, PyPIDistribution):
+            raise TypeError("cannot compare %s and %s"
+                % (type(self).__name__, type(other).__name__))
+        elif self.name != other.name:
+            raise TypeError("cannot compare %s and %s"
+                % (self.name, other.name))
+
+    def __eq__(self, other):
+        self._check_is_comparable(other)
+        return self.version == other.version
+
+    def __lt__(self, other):
+        self._check_is_comparable(other)
+        return self.version < other.version
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __gt__(self, other):
+        return not (self.__lt__(other) or self.__eq__(other))
+
+    def __le__(self, other):
+        return self.__eq__(other) or self.__lt__(other)
+
+    def __ge__(self, other):
+        return self.__eq__(other) or self.__gt__(other)
+
+    # See http://docs.python.org/reference/datamodel#object.__hash__
+    __hash__ = object.__hash__
+
+
+class PyPIDistributions(list):
+    """A container of PyPIDistribution objects.
+
+    Contains methods and facilities to sort and filter distributions.
+    """
+    def __init__(self, list=[]):
+        # To disable the ability to pass lists on instanciation
+        super(PyPIDistributions, self).__init__()
+        for item in list:
+            self.append(item)
+
+    def filter(self, predicate):
+        """Filter the distributions and return a subset of distributions that
+        match the given predicate
+        """
+        return PyPIDistributions(
+            [dist for dist in self if dist.name == predicate.name and
+            predicate.match(dist.version)])
+
+    def get_last(self, predicate, prefer_source=None, prefer_final=None):
+        """Return the most up to date version, that satisfy the given
+        predicate
+        """
+        distributions = self.filter(predicate)
+        distributions.sort_distributions(prefer_source, prefer_final, reverse=True)
+        return distributions[0]
+
+    def get_same_name_and_version(self):
+        """Return lists of PyPIDistribution objects that refer to the same
+        name and version number. This do not consider the type (source, binary,
+        etc.)"""
+        processed = []
+        duplicates = []
+        for dist in self:
+            if (dist.name, dist.version) not in processed:
+                processed.append((dist.name, dist.version))
+                found_duplicates = [d for d in self if d.name == dist.name and
+                                    d.version == dist.version]
+                if len(found_duplicates) > 1:
+                    duplicates.append(found_duplicates)
+        return duplicates
+
+    def append(self, o):
+        """Append a new distribution to the list.
+
+        If a distribution with the same name and version exists, just grab the
+        URL informations and add a new new url for the existing one.
+        """
+        similar_dists = [d for d in self if d.name == o.name and
+                         d.version == o.version and d.type == o.type]
+        if len(similar_dists) > 0:
+            dist = similar_dists[0]
+            dist.add_url(**o.url)
+        else:
+            super(PyPIDistributions, self).append(o)
+
+    def sort_distributions(self, prefer_source=True, prefer_final=False,
+                           reverse=True, *args, **kwargs):
+        """order the results with the given properties"""
+
+        sort_by = []
+        if prefer_final:
+            sort_by.append("is_final")
+        sort_by.append("version")
+
+        if prefer_source:
+            sort_by.append("is_source")
+
+        super(PyPIDistributions, self).sort(
+            key=lambda i: [getattr(i, arg) for arg in sort_by],
+            reverse=reverse, *args, **kwargs)
+
+
+def split_archive_name(archive_name, probable_name=None):
+    """Split an archive name into two parts: name and version.
+
+    Return the tuple (name, version)
+    """
+    # Try to determine wich part is the name and wich is the version using the
+    # "-" separator. Take the larger part to be the version number then reduce
+    # if this not works.
+    def eager_split(str, maxsplit=2):
+        # split using the "-" separator
+        splits = str.rsplit("-", maxsplit)
+        name = splits[0]
+        version = "-".join(splits[1:])
+        if version.startswith("-"):
+            version = version[1:]
+        if suggest_normalized_version(version) is None and maxsplit >= 0:
+            # we dont get a good version number: recurse !
+            return eager_split(str, maxsplit - 1)
+        else:
+            return (name, version)
+    if probable_name is not None:
+        probable_name = probable_name.lower()
+    name = None
+    if probable_name is not None and probable_name in archive_name:
+        # we get the name from probable_name, if given.
+        name = probable_name
+        version = archive_name.lstrip(name)
+    else:
+        name, version = eager_split(archive_name)
+
+    version = suggest_normalized_version(version)
+    if version != "" and name != "":
+        return (name.lower(), version)
+    else:
+        raise CantParseArchiveName(archive_name)
diff --git a/src/distutils2/pypi/errors.py b/src/distutils2/pypi/errors.py
new file mode 100644
--- /dev/null
+++ b/src/distutils2/pypi/errors.py
@@ -0,0 +1,33 @@
+"""distutils2.pypi.errors
+
+All errors and exceptions raised by PyPiIndex classes.
+"""
+from distutils2.errors import DistutilsError
+
+
+class PyPIError(DistutilsError):
+    """The base class for errors of the pypi python package."""
+
+
+class DistributionNotFound(PyPIError):
+    """No distribution match the given requirements."""
+
+
+class CantParseArchiveName(PyPIError):
+    """An archive name can't be parsed to find distribution name and version"""
+
+
+class DownloadError(PyPIError):
+    """An error has occurs while downloading"""
+
+
+class HashDoesNotMatch(DownloadError):
+    """Compared hashes does not match"""
+
+
+class UnsupportedHashName(PyPIError):
+    """A unsupported hashname has been used"""
+
+
+class UnableToDownload(PyPIError):
+    """All mirrors have been tried, without success"""
diff --git a/src/distutils2/pypi/pkg_resources.py b/src/distutils2/pypi/pkg_resources.py
deleted file mode 100644
--- a/src/distutils2/pypi/pkg_resources.py
+++ /dev/null
@@ -1,2693 +0,0 @@
-"""Package resource API
---------------------
-
-A resource is a logical file contained within a package, or a logical
-subdirectory thereof.  The package resource API expects resource names
-to have their path parts separated with ``/``, *not* whatever the local
-path separator is.  Do not use os.path operations to manipulate resource
-names being passed into the API.
-
-The package resource API is designed to work with normal filesystem packages,
-.egg files, and unpacked .egg files.  It can also work in a limited way with
-.zip files and with custom PEP 302 loaders that support the ``get_data()``
-method.
-"""
-
-import sys, os, zipimport, time, re, imp, types
-from urlparse import urlparse, urlunparse
-
-try:
-    frozenset
-except NameError:
-    from sets import ImmutableSet as frozenset
-
-# capture these to bypass sandboxing
-from os import utime
-try:
-    from os import mkdir, rename, unlink
-    WRITE_SUPPORT = True
-except ImportError:
-    # no write support, probably under GAE
-    WRITE_SUPPORT = False
-
-from os import open as os_open
-from os.path import isdir, split
-
-# This marker is used to simplify the process that checks is the
-# setuptools package was installed by the Setuptools project
-# or by the Distribute project, in case Setuptools creates
-# a distribution with the same version.
-#
-# The bootstrapping script for instance, will check if this
-# attribute is present to decide wether to reinstall the package
-_distribute = True
-
-def _bypass_ensure_directory(name, mode=0777):
-    # Sandbox-bypassing version of ensure_directory()
-    if not WRITE_SUPPORT:
-        raise IOError('"os.mkdir" not supported on this platform.')
-    dirname, filename = split(name)
-    if dirname and filename and not isdir(dirname):
-        _bypass_ensure_directory(dirname)
-        mkdir(dirname, mode)
-
-
-
-
-
-
-
-
-def get_supported_platform():
-    """Return this platform's maximum compatible version.
-
-    distutils.util.get_platform() normally reports the minimum version
-    of Mac OS X that would be required to *use* extensions produced by
-    distutils.  But what we want when checking compatibility is to know the
-    version of Mac OS X that we are *running*.  To allow usage of packages that
-    explicitly require a newer version of Mac OS X, we must also know the
-    current version of the OS.
-
-    If this condition occurs for any other platform with a version in its
-    platform strings, this function should be extended accordingly.
-    """
-    plat = get_build_platform(); m = macosVersionString.match(plat)
-    if m is not None and sys.platform == "darwin":
-        try:
-            plat = 'macosx-%s-%s' % ('.'.join(_macosx_vers()[:2]), m.group(3))
-        except ValueError:
-            pass    # not Mac OS X
-    return plat
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-__all__ = [
-    # Basic resource access and distribution/entry point discovery
-    'require', 'run_script', 'get_provider',  'get_distribution',
-    'load_entry_point', 'get_entry_map', 'get_entry_info', 'iter_entry_points',
-    'resource_string', 'resource_stream', 'resource_filename',
-    'resource_listdir', 'resource_exists', 'resource_isdir',
-
-    # Environmental control
-    'declare_namespace', 'working_set', 'add_activation_listener',
-    'find_distributions', 'set_extraction_path', 'cleanup_resources',
-    'get_default_cache',
-
-    # Primary implementation classes
-    'Environment', 'WorkingSet', 'ResourceManager',
-    'Distribution', 'Requirement', 'EntryPoint',
-
-    # Exceptions
-    'ResolutionError','VersionConflict','DistributionNotFound','UnknownExtra',
-    'ExtractionError',
-
-    # Parsing functions and string utilities
-    'parse_requirements', 'parse_version', 'safe_name', 'safe_version',
-    'get_platform', 'compatible_platforms', 'yield_lines', 'split_sections',
-    'safe_extra', 'to_filename',
-
-    # filesystem utilities
-    'ensure_directory', 'normalize_path',
-
-    # Distribution "precedence" constants
-    'EGG_DIST', 'BINARY_DIST', 'SOURCE_DIST', 'CHECKOUT_DIST', 'DEVELOP_DIST',
-
-    # "Provider" interfaces, implementations, and registration/lookup APIs
-    'IMetadataProvider', 'IResourceProvider', 'FileMetadata',
-    'PathMetadata', 'EggMetadata', 'EmptyProvider', 'empty_provider',
-    'NullProvider', 'EggProvider', 'DefaultProvider', 'ZipProvider',
-    'register_finder', 'register_namespace_handler', 'register_loader_type',
-    'fixup_namespace_packages', 'get_importer',
-
-    # Deprecated/backward compatibility only
-    'run_main', 'AvailableDistributions',
-]
-class ResolutionError(Exception):
-    """Abstract base for dependency resolution errors"""
-    def __repr__(self):
-        return self.__class__.__name__+repr(self.args)
-
-class VersionConflict(ResolutionError):
-    """An already-installed version conflicts with the requested version"""
-
-class DistributionNotFound(ResolutionError):
-    """A requested distribution was not found"""
-
-class UnknownExtra(ResolutionError):
-    """Distribution doesn't have an "extra feature" of the given name"""
-_provider_factories = {}
-
-PY_MAJOR = sys.version[:3]
-EGG_DIST    = 3
-BINARY_DIST = 2
-SOURCE_DIST = 1
-CHECKOUT_DIST = 0
-DEVELOP_DIST = -1
-
-def register_loader_type(loader_type, provider_factory):
-    """Register `provider_factory` to make providers for `loader_type`
-
-    `loader_type` is the type or class of a PEP 302 ``module.__loader__``,
-    and `provider_factory` is a function that, passed a *module* object,
-    returns an ``IResourceProvider`` for that module.
-    """
-    _provider_factories[loader_type] = provider_factory
-
-def get_provider(moduleOrReq):
-    """Return an IResourceProvider for the named module or requirement"""
-    if isinstance(moduleOrReq,Requirement):
-        return working_set.find(moduleOrReq) or require(str(moduleOrReq))[0]
-    try:
-        module = sys.modules[moduleOrReq]
-    except KeyError:
-        __import__(moduleOrReq)
-        module = sys.modules[moduleOrReq]
-    loader = getattr(module, '__loader__', None)
-    return _find_adapter(_provider_factories, loader)(module)
-
-def _macosx_vers(_cache=[]):
-    if not _cache:
-        import platform
-        version = platform.mac_ver()[0]
-        # fallback for MacPorts
-        if version == '':
-            import plistlib
-            plist = '/System/Library/CoreServices/SystemVersion.plist'
-            if os.path.exists(plist):
-                if hasattr(plistlib, 'readPlist'):
-                    plist_content = plistlib.readPlist(plist)
-                    if 'ProductVersion' in plist_content:
-                        version = plist_content['ProductVersion']
-
-        _cache.append(version.split('.'))
-    return _cache[0]
-
-def _macosx_arch(machine):
-    return {'PowerPC':'ppc', 'Power_Macintosh':'ppc'}.get(machine,machine)
-
-def get_build_platform():
-    """Return this platform's string for platform-specific distributions
-
-    XXX Currently this is the same as ``distutils.util.get_platform()``, but it
-    needs some hacks for Linux and Mac OS X.
-    """
-    try:
-        from distutils.util import get_platform
-    except ImportError:
-        from sysconfig import get_platform
-
-    plat = get_platform()
-    if sys.platform == "darwin" and not plat.startswith('macosx-'):
-        try:
-            version = _macosx_vers()
-            machine = os.uname()[4].replace(" ", "_")
-            return "macosx-%d.%d-%s" % (int(version[0]), int(version[1]),
-                _macosx_arch(machine))
-        except ValueError:
-            # if someone is running a non-Mac darwin system, this will fall
-            # through to the default implementation
-            pass
-    return plat
-
-macosVersionString = re.compile(r"macosx-(\d+)\.(\d+)-(.*)")
-darwinVersionString = re.compile(r"darwin-(\d+)\.(\d+)\.(\d+)-(.*)")
-get_platform = get_build_platform   # XXX backward compat
-
-def compatible_platforms(provided,required):
-    """Can code for the `provided` platform run on the `required` platform?
-
-    Returns true if either platform is ``None``, or the platforms are equal.
-
-    XXX Needs compatibility checks for Linux and other unixy OSes.
-    """
-    if provided is None or required is None or provided==required:
-        return True     # easy case
-
-    # Mac OS X special cases
-    reqMac = macosVersionString.match(required)
-    if reqMac:
-        provMac = macosVersionString.match(provided)
-
-        # is this a Mac package?
-        if not provMac:
-            # this is backwards compatibility for packages built before
-            # setuptools 0.6. All packages built after this point will
-            # use the new macosx designation.
-            provDarwin = darwinVersionString.match(provided)
-            if provDarwin:
-                dversion = int(provDarwin.group(1))
-                macosversion = "%s.%s" % (reqMac.group(1), reqMac.group(2))
-                if dversion == 7 and macosversion >= "10.3" or \
-                    dversion == 8 and macosversion >= "10.4":
-
-                    #import warnings
-                    #warnings.warn("Mac eggs should be rebuilt to "
-                    #    "use the macosx designation instead of darwin.",
-                    #    category=DeprecationWarning)
-                    return True
-            return False    # egg isn't macosx or legacy darwin
-
-        # are they the same major version and machine type?
-        if provMac.group(1) != reqMac.group(1) or \
-            provMac.group(3) != reqMac.group(3):
-            return False
-
-
-
-        # is the required OS major update >= the provided one?
-        if int(provMac.group(2)) > int(reqMac.group(2)):
-            return False
-
-        return True
-
-    # XXX Linux and other platforms' special cases should go here
-    return False
-
-
-def run_script(dist_spec, script_name):
-    """Locate distribution `dist_spec` and run its `script_name` script"""
-    ns = sys._getframe(1).f_globals
-    name = ns['__name__']
-    ns.clear()
-    ns['__name__'] = name
-    require(dist_spec)[0].run_script(script_name, ns)
-
-run_main = run_script   # backward compatibility
-
-def get_distribution(dist):
-    """Return a current distribution object for a Requirement or string"""
-    if isinstance(dist,basestring): dist = Requirement.parse(dist)
-    if isinstance(dist,Requirement): dist = get_provider(dist)
-    if not isinstance(dist,Distribution):
-        raise TypeError("Expected string, Requirement, or Distribution", dist)
-    return dist
-
-def load_entry_point(dist, group, name):
-    """Return `name` entry point of `group` for `dist` or raise ImportError"""
-    return get_distribution(dist).load_entry_point(group, name)
-
-def get_entry_map(dist, group=None):
-    """Return the entry point map for `group`, or the full entry map"""
-    return get_distribution(dist).get_entry_map(group)
-
-def get_entry_info(dist, group, name):
-    """Return the EntryPoint object for `group`+`name`, or ``None``"""
-    return get_distribution(dist).get_entry_info(group, name)
-
-
-class IMetadataProvider:
-
-    def has_metadata(name):
-        """Does the package's distribution contain the named metadata?"""
-
-    def get_metadata(name):
-        """The named metadata resource as a string"""
-
-    def get_metadata_lines(name):
-        """Yield named metadata resource as list of non-blank non-comment lines
-
-       Leading and trailing whitespace is stripped from each line, and lines
-       with ``#`` as the first non-blank character are omitted."""
-
-    def metadata_isdir(name):
-        """Is the named metadata a directory?  (like ``os.path.isdir()``)"""
-
-    def metadata_listdir(name):
-        """List of metadata names in the directory (like ``os.listdir()``)"""
-
-    def run_script(script_name, namespace):
-        """Execute the named script in the supplied namespace dictionary"""
-
-
-
-
-
-
-
-
-
-
-class IResourceProvider(IMetadataProvider):
-    """An object that provides access to package resources"""
-
-    def get_resource_filename(manager, resource_name):
-        """Return a true filesystem path for `resource_name`
-
-        `manager` must be an ``IResourceManager``"""
-
-    def get_resource_stream(manager, resource_name):
-        """Return a readable file-like object for `resource_name`
-
-        `manager` must be an ``IResourceManager``"""
-
-    def get_resource_string(manager, resource_name):
-        """Return a string containing the contents of `resource_name`
-
-        `manager` must be an ``IResourceManager``"""
-
-    def has_resource(resource_name):
-        """Does the package contain the named resource?"""
-
-    def resource_isdir(resource_name):
-        """Is the named resource a directory?  (like ``os.path.isdir()``)"""
-
-    def resource_listdir(resource_name):
-        """List of resource names in the directory (like ``os.listdir()``)"""
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-class WorkingSet(object):
-    """A collection of active distributions on sys.path (or a similar list)"""
-
-    def __init__(self, entries=None):
-        """Create working set from list of path entries (default=sys.path)"""
-        self.entries = []
-        self.entry_keys = {}
-        self.by_key = {}
-        self.callbacks = []
-
-        if entries is None:
-            entries = sys.path
-
-        for entry in entries:
-            self.add_entry(entry)
-
-
-    def add_entry(self, entry):
-        """Add a path item to ``.entries``, finding any distributions on it
-
-        ``find_distributions(entry,True)`` is used to find distributions
-        corresponding to the path entry, and they are added.  `entry` is
-        always appended to ``.entries``, even if it is already present.
-        (This is because ``sys.path`` can contain the same value more than
-        once, and the ``.entries`` of the ``sys.path`` WorkingSet should always
-        equal ``sys.path``.)
-        """
-        self.entry_keys.setdefault(entry, [])
-        self.entries.append(entry)
-        for dist in find_distributions(entry, True):
-            self.add(dist, entry, False)
-
-
-    def __contains__(self,dist):
-        """True if `dist` is the active distribution for its project"""
-        return self.by_key.get(dist.key) == dist
-
-
-
-
-
-    def find(self, req):
-        """Find a distribution matching requirement `req`
-
-        If there is an active distribution for the requested project, this
-        returns it as long as it meets the version requirement specified by
-        `req`.  But, if there is an active distribution for the project and it
-        does *not* meet the `req` requirement, ``VersionConflict`` is raised.
-        If there is no active distribution for the requested project, ``None``
-        is returned.
-        """
-        dist = self.by_key.get(req.key)
-        if dist is not None and dist not in req:
-            raise VersionConflict(dist,req)     # XXX add more info
-        else:
-            return dist
-
-    def iter_entry_points(self, group, name=None):
-        """Yield entry point objects from `group` matching `name`
-
-        If `name` is None, yields all entry points in `group` from all
-        distributions in the working set, otherwise only ones matching
-        both `group` and `name` are yielded (in distribution order).
-        """
-        for dist in self:
-            entries = dist.get_entry_map(group)
-            if name is None:
-                for ep in entries.values():
-                    yield ep
-            elif name in entries:
-                yield entries[name]
-
-    def run_script(self, requires, script_name):
-        """Locate distribution for `requires` and run `script_name` script"""
-        ns = sys._getframe(1).f_globals
-        name = ns['__name__']
-        ns.clear()
-        ns['__name__'] = name
-        self.require(requires)[0].run_script(script_name, ns)
-
-
-
-    def __iter__(self):
-        """Yield distributions for non-duplicate projects in the working set
-
-        The yield order is the order in which the items' path entries were
-        added to the working set.
-        """
-        seen = {}
-        for item in self.entries:
-            for key in self.entry_keys[item]:
-                if key not in seen:
-                    seen[key]=1
-                    yield self.by_key[key]
-
-    def add(self, dist, entry=None, insert=True):
-        """Add `dist` to working set, associated with `entry`
-
-        If `entry` is unspecified, it defaults to the ``.location`` of `dist`.
-        On exit from this routine, `entry` is added to the end of the working
-        set's ``.entries`` (if it wasn't already present).
-
-        `dist` is only added to the working set if it's for a project that
-        doesn't already have a distribution in the set.  If it's added, any
-        callbacks registered with the ``subscribe()`` method will be called.
-        """
-        if insert:
-            dist.insert_on(self.entries, entry)
-
-        if entry is None:
-            entry = dist.location
-        keys = self.entry_keys.setdefault(entry,[])
-        keys2 = self.entry_keys.setdefault(dist.location,[])
-        if dist.key in self.by_key:
-            return      # ignore hidden distros
-
-        self.by_key[dist.key] = dist
-        if dist.key not in keys:
-            keys.append(dist.key)
-        if dist.key not in keys2:
-            keys2.append(dist.key)
-        self._added_new(dist)
-
-    def resolve(self, requirements, env=None, installer=None, replacement=True):
-        """List all distributions needed to (recursively) meet `requirements`
-
-        `requirements` must be a sequence of ``Requirement`` objects.  `env`,
-        if supplied, should be an ``Environment`` instance.  If
-        not supplied, it defaults to all distributions available within any
-        entry or distribution in the working set.  `installer`, if supplied,
-        will be invoked with each requirement that cannot be met by an
-        already-installed distribution; it should return a ``Distribution`` or
-        ``None``.
-        """
-
-        requirements = list(requirements)[::-1]  # set up the stack
-        processed = {}  # set of processed requirements
-        best = {}  # key -> dist
-        to_activate = []
-
-        while requirements:
-            req = requirements.pop(0)   # process dependencies breadth-first
-            if _override_setuptools(req) and replacement:
-                req = Requirement.parse('distribute')
-
-            if req in processed:
-                # Ignore cyclic or redundant dependencies
-                continue
-            dist = best.get(req.key)
-            if dist is None:
-                # Find the best distribution and add it to the map
-                dist = self.by_key.get(req.key)
-                if dist is None:
-                    if env is None:
-                        env = Environment(self.entries)
-                    dist = best[req.key] = env.best_match(req, self, installer)
-                    if dist is None:
-                        #msg = ("The '%s' distribution was not found on this "
-                        #       "system, and is required by this application.")
-                        #raise DistributionNotFound(msg % req)
-
-                        # unfortunately, zc.buildout uses a str(err)
-                        # to get the name of the distribution here..
-                        raise DistributionNotFound(req)
-                to_activate.append(dist)
-            if dist not in req:
-                # Oops, the "best" so far conflicts with a dependency
-                raise VersionConflict(dist,req) # XXX put more info here
-            requirements.extend(dist.requires(req.extras)[::-1])
-            processed[req] = True
-
-        return to_activate    # return list of distros to activate
-
-    def find_plugins(self,
-        plugin_env, full_env=None, installer=None, fallback=True
-    ):
-        """Find all activatable distributions in `plugin_env`
-
-        Example usage::
-
-            distributions, errors = working_set.find_plugins(
-                Environment(plugin_dirlist)
-            )
-            map(working_set.add, distributions)  # add plugins+libs to sys.path
-            print 'Could not load', errors        # display errors
-
-        The `plugin_env` should be an ``Environment`` instance that contains
-        only distributions that are in the project's "plugin directory" or
-        directories. The `full_env`, if supplied, should be an ``Environment``
-        contains all currently-available distributions.  If `full_env` is not
-        supplied, one is created automatically from the ``WorkingSet`` this
-        method is called on, which will typically mean that every directory on
-        ``sys.path`` will be scanned for distributions.
-
-        `installer` is a standard installer callback as used by the
-        ``resolve()`` method. The `fallback` flag indicates whether we should
-        attempt to resolve older versions of a plugin if the newest version
-        cannot be resolved.
-
-        This method returns a 2-tuple: (`distributions`, `error_info`), where
-        `distributions` is a list of the distributions found in `plugin_env`
-        that were loadable, along with any other distributions that are needed
-        to resolve their dependencies.  `error_info` is a dictionary mapping
-        unloadable plugin distributions to an exception instance describing the
-        error that occurred. Usually this will be a ``DistributionNotFound`` or
-        ``VersionConflict`` instance.
-        """
-
-        plugin_projects = list(plugin_env)
-        plugin_projects.sort()  # scan project names in alphabetic order
-
-        error_info = {}
-        distributions = {}
-
-        if full_env is None:
-            env = Environment(self.entries)
-            env += plugin_env
-        else:
-            env = full_env + plugin_env
-
-        shadow_set = self.__class__([])
-        map(shadow_set.add, self)   # put all our entries in shadow_set
-
-        for project_name in plugin_projects:
-
-            for dist in plugin_env[project_name]:
-
-                req = [dist.as_requirement()]
-
-                try:
-                    resolvees = shadow_set.resolve(req, env, installer)
-
-                except ResolutionError,v:
-                    error_info[dist] = v    # save error info
-                    if fallback:
-                        continue    # try the next older version of project
-                    else:
-                        break       # give up on this project, keep going
-
-                else:
-                    map(shadow_set.add, resolvees)
-                    distributions.update(dict.fromkeys(resolvees))
-
-                    # success, no need to try any more versions of this project
-                    break
-
-        distributions = list(distributions)
-        distributions.sort()
-
-        return distributions, error_info
-
-
-
-
-
-    def require(self, *requirements):
-        """Ensure that distributions matching `requirements` are activated
-
-        `requirements` must be a string or a (possibly-nested) sequence
-        thereof, specifying the distributions and versions required.  The
-        return value is a sequence of the distributions that needed to be
-        activated to fulfill the requirements; all relevant distributions are
-        included, even if they were already activated in this working set.
-        """
-
-        needed = self.resolve(parse_requirements(requirements))
-
-        for dist in needed:
-            self.add(dist)
-
-        return needed
-
-
-    def subscribe(self, callback):
-        """Invoke `callback` for all distributions (including existing ones)"""
-        if callback in self.callbacks:
-            return
-        self.callbacks.append(callback)
-        for dist in self:
-            callback(dist)
-
-
-    def _added_new(self, dist):
-        for callback in self.callbacks:
-            callback(dist)
-
-
-
-
-
-
-
-
-
-
-
-class Environment(object):
-    """Searchable snapshot of distributions on a search path"""
-
-    def __init__(self, search_path=None, platform=get_supported_platform(), python=PY_MAJOR):
-        """Snapshot distributions available on a search path
-
-        Any distributions found on `search_path` are added to the environment.
-        `search_path` should be a sequence of ``sys.path`` items.  If not
-        supplied, ``sys.path`` is used.
-
-        `platform` is an optional string specifying the name of the platform
-        that platform-specific distributions must be compatible with.  If
-        unspecified, it defaults to the current platform.  `python` is an
-        optional string naming the desired version of Python (e.g. ``'2.4'``);
-        it defaults to the current version.
-
-        You may explicitly set `platform` (and/or `python`) to ``None`` if you
-        wish to map *all* distributions, not just those compatible with the
-        running platform or Python version.
-        """
-        self._distmap = {}
-        self._cache = {}
-        self.platform = platform
-        self.python = python
-        self.scan(search_path)
-
-    def can_add(self, dist):
-        """Is distribution `dist` acceptable for this environment?
-
-        The distribution must match the platform and python version
-        requirements specified when this environment was created, or False
-        is returned.
-        """
-        return (self.python is None or dist.py_version is None
-            or dist.py_version==self.python) \
-           and compatible_platforms(dist.platform,self.platform)
-
-    def remove(self, dist):
-        """Remove `dist` from the environment"""
-        self._distmap[dist.key].remove(dist)
-
-    def scan(self, search_path=None):
-        """Scan `search_path` for distributions usable in this environment
-
-        Any distributions found are added to the environment.
-        `search_path` should be a sequence of ``sys.path`` items.  If not
-        supplied, ``sys.path`` is used.  Only distributions conforming to
-        the platform/python version defined at initialization are added.
-        """
-        if search_path is None:
-            search_path = sys.path
-
-        for item in search_path:
-            for dist in find_distributions(item):
-                self.add(dist)
-
-    def __getitem__(self,project_name):
-        """Return a newest-to-oldest list of distributions for `project_name`
-        """
-        try:
-            return self._cache[project_name]
-        except KeyError:
-            project_name = project_name.lower()
-            if project_name not in self._distmap:
-                return []
-
-        if project_name not in self._cache:
-            dists = self._cache[project_name] = self._distmap[project_name]
-            _sort_dists(dists)
-
-        return self._cache[project_name]
-
-    def add(self,dist):
-        """Add `dist` if we ``can_add()`` it and it isn't already added"""
-        if self.can_add(dist) and dist.has_version():
-            dists = self._distmap.setdefault(dist.key,[])
-            if dist not in dists:
-                dists.append(dist)
-                if dist.key in self._cache:
-                    _sort_dists(self._cache[dist.key])
-
-
-    def best_match(self, req, working_set, installer=None):
-        """Find distribution best matching `req` and usable on `working_set`
-
-        This calls the ``find(req)`` method of the `working_set` to see if a
-        suitable distribution is already active.  (This may raise
-        ``VersionConflict`` if an unsuitable version of the project is already
-        active in the specified `working_set`.)  If a suitable distribution
-        isn't active, this method returns the newest distribution in the
-        environment that meets the ``Requirement`` in `req`.  If no suitable
-        distribution is found, and `installer` is supplied, then the result of
-        calling the environment's ``obtain(req, installer)`` method will be
-        returned.
-        """
-        dist = working_set.find(req)
-        if dist is not None:
-            return dist
-        for dist in self[req.key]:
-            if dist in req:
-                return dist
-        return self.obtain(req, installer) # try and download/install
-
-    def obtain(self, requirement, installer=None):
-        """Obtain a distribution matching `requirement` (e.g. via download)
-
-        Obtain a distro that matches requirement (e.g. via download).  In the
-        base ``Environment`` class, this routine just returns
-        ``installer(requirement)``, unless `installer` is None, in which case
-        None is returned instead.  This method is a hook that allows subclasses
-        to attempt other ways of obtaining a distribution before falling back
-        to the `installer` argument."""
-        if installer is not None:
-            return installer(requirement)
-
-    def __iter__(self):
-        """Yield the unique project names of the available distributions"""
-        for key in self._distmap.keys():
-            if self[key]: yield key
-
-
-
-
-    def __iadd__(self, other):
-        """In-place addition of a distribution or environment"""
-        if isinstance(other,Distribution):
-            self.add(other)
-        elif isinstance(other,Environment):
-            for project in other:
-                for dist in other[project]:
-                    self.add(dist)
-        else:
-            raise TypeError("Can't add %r to environment" % (other,))
-        return self
-
-    def __add__(self, other):
-        """Add an environment or distribution to an environment"""
-        new = self.__class__([], platform=None, python=None)
-        for env in self, other:
-            new += env
-        return new
-
-
-AvailableDistributions = Environment    # XXX backward compatibility
-
-
-class ExtractionError(RuntimeError):
-    """An error occurred extracting a resource
-
-    The following attributes are available from instances of this exception:
-
-    manager
-        The resource manager that raised this exception
-
-    cache_path
-        The base directory for resource extraction
-
-    original_error
-        The exception instance that caused extraction to fail
-    """
-
-
-
-
-class ResourceManager:
-    """Manage resource extraction and packages"""
-    extraction_path = None
-
-    def __init__(self):
-        self.cached_files = {}
-
-    def resource_exists(self, package_or_requirement, resource_name):
-        """Does the named resource exist?"""
-        return get_provider(package_or_requirement).has_resource(resource_name)
-
-    def resource_isdir(self, package_or_requirement, resource_name):
-        """Is the named resource an existing directory?"""
-        return get_provider(package_or_requirement).resource_isdir(
-            resource_name
-        )
-
-    def resource_filename(self, package_or_requirement, resource_name):
-        """Return a true filesystem path for specified resource"""
-        return get_provider(package_or_requirement).get_resource_filename(
-            self, resource_name
-        )
-
-    def resource_stream(self, package_or_requirement, resource_name):
-        """Return a readable file-like object for specified resource"""
-        return get_provider(package_or_requirement).get_resource_stream(
-            self, resource_name
-        )
-
-    def resource_string(self, package_or_requirement, resource_name):
-        """Return specified resource as a string"""
-        return get_provider(package_or_requirement).get_resource_string(
-            self, resource_name
-        )
-
-    def resource_listdir(self, package_or_requirement, resource_name):
-        """List the contents of the named resource directory"""
-        return get_provider(package_or_requirement).resource_listdir(
-            resource_name
-        )
-
-    def extraction_error(self):
-        """Give an error message for problems extracting file(s)"""
-
-        old_exc = sys.exc_info()[1]
-        cache_path = self.extraction_path or get_default_cache()
-
-        err = ExtractionError("""Can't extract file(s) to egg cache
-
-The following error occurred while trying to extract file(s) to the Python egg
-cache:
-
-  %s
-
-The Python egg cache directory is currently set to:
-
-  %s
-
-Perhaps your account does not have write access to this directory?  You can
-change the cache directory by setting the PYTHON_EGG_CACHE environment
-variable to point to an accessible directory.
-"""         % (old_exc, cache_path)
-        )
-        err.manager        = self
-        err.cache_path     = cache_path
-        err.original_error = old_exc
-        raise err
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-    def get_cache_path(self, archive_name, names=()):
-        """Return absolute location in cache for `archive_name` and `names`
-
-        The parent directory of the resulting path will be created if it does
-        not already exist.  `archive_name` should be the base filename of the
-        enclosing egg (which may not be the name of the enclosing zipfile!),
-        including its ".egg" extension.  `names`, if provided, should be a
-        sequence of path name parts "under" the egg's extraction location.
-
-        This method should only be called by resource providers that need to
-        obtain an extraction location, and only for names they intend to
-        extract, as it tracks the generated names for possible cleanup later.
-        """
-        extract_path = self.extraction_path or get_default_cache()
-        target_path = os.path.join(extract_path, archive_name+'-tmp', *names)
-        try:
-            _bypass_ensure_directory(target_path)
-        except:
-            self.extraction_error()
-
-        self.cached_files[target_path] = 1
-        return target_path
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-    def postprocess(self, tempname, filename):
-        """Perform any platform-specific postprocessing of `tempname`
-
-        This is where Mac header rewrites should be done; other platforms don't
-        have anything special they should do.
-
-        Resource providers should call this method ONLY after successfully
-        extracting a compressed resource.  They must NOT call it on resources
-        that are already in the filesystem.
-
-        `tempname` is the current (temporary) name of the file, and `filename`
-        is the name it will be renamed to by the caller after this routine
-        returns.
-        """
-
-        if os.name == 'posix':
-            # Make the resource executable
-            mode = ((os.stat(tempname).st_mode) | 0555) & 07777
-            os.chmod(tempname, mode)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-    def set_extraction_path(self, path):
-        """Set the base path where resources will be extracted to, if needed.
-
-        If you do not call this routine before any extractions take place, the
-        path defaults to the return value of ``get_default_cache()``.  (Which
-        is based on the ``PYTHON_EGG_CACHE`` environment variable, with various
-        platform-specific fallbacks.  See that routine's documentation for more
-        details.)
-
-        Resources are extracted to subdirectories of this path based upon
-        information given by the ``IResourceProvider``.  You may set this to a
-        temporary directory, but then you must call ``cleanup_resources()`` to
-        delete the extracted files when done.  There is no guarantee that
-        ``cleanup_resources()`` will be able to remove all extracted files.
-
-        (Note: you may not change the extraction path for a given resource
-        manager once resources have been extracted, unless you first call
-        ``cleanup_resources()``.)
-        """
-        if self.cached_files:
-            raise ValueError(
-                "Can't change extraction path, files already extracted"
-            )
-
-        self.extraction_path = path
-
-    def cleanup_resources(self, force=False):
-        """
-        Delete all extracted resource files and directories, returning a list
-        of the file and directory names that could not be successfully removed.
-        This function does not have any concurrency protection, so it should
-        generally only be called when the extraction path is a temporary
-        directory exclusive to a single process.  This method is not
-        automatically called; you must call it explicitly or register it as an
-        ``atexit`` function if you wish to ensure cleanup of a temporary
-        directory used for extractions.
-        """
-        # XXX
-
-
-
-def get_default_cache():
-    """Determine the default cache location
-
-    This returns the ``PYTHON_EGG_CACHE`` environment variable, if set.
-    Otherwise, on Windows, it returns a "Python-Eggs" subdirectory of the
-    "Application Data" directory.  On all other systems, it's "~/.python-eggs".
-    """
-    try:
-        return os.environ['PYTHON_EGG_CACHE']
-    except KeyError:
-        pass
-
-    if os.name!='nt':
-        return os.path.expanduser('~/.python-eggs')
-
-    app_data = 'Application Data'   # XXX this may be locale-specific!
-    app_homes = [
-        (('APPDATA',), None),       # best option, should be locale-safe
-        (('USERPROFILE',), app_data),
-        (('HOMEDRIVE','HOMEPATH'), app_data),
-        (('HOMEPATH',), app_data),
-        (('HOME',), None),
-        (('WINDIR',), app_data),    # 95/98/ME
-    ]
-
-    for keys, subdir in app_homes:
-        dirname = ''
-        for key in keys:
-            if key in os.environ:
-                dirname = os.path.join(dirname, os.environ[key])
-            else:
-                break
-        else:
-            if subdir:
-                dirname = os.path.join(dirname,subdir)
-            return os.path.join(dirname, 'Python-Eggs')
-    else:
-        raise RuntimeError(
-            "Please set the PYTHON_EGG_CACHE enviroment variable"
-        )
-
-def safe_name(name):
-    """Convert an arbitrary string to a standard distribution name
-
-    Any runs of non-alphanumeric/. characters are replaced with a single '-'.
-    """
-    return re.sub('[^A-Za-z0-9.]+', '-', name)
-
-
-def safe_version(version):
-    """Convert an arbitrary string to a standard version string
-
-    Spaces become dots, and all other non-alphanumeric characters become
-    dashes, with runs of multiple dashes condensed to a single dash.
-    """
-    version = version.replace(' ','.')
-    return re.sub('[^A-Za-z0-9.]+', '-', version)
-
-
-def safe_extra(extra):
-    """Convert an arbitrary string to a standard 'extra' name
-
-    Any runs of non-alphanumeric characters are replaced with a single '_',
-    and the result is always lowercased.
-    """
-    return re.sub('[^A-Za-z0-9.]+', '_', extra).lower()
-
-
-def to_filename(name):
-    """Convert a project or version name to its filename-escaped form
-
-    Any '-' characters are currently replaced with '_'.
-    """
-    return name.replace('-','_')
-
-
-
-
-
-
-
-
-class NullProvider:
-    """Try to implement resources and metadata for arbitrary PEP 302 loaders"""
-
-    egg_name = None
-    egg_info = None
-    loader = None
-
-    def __init__(self, module):
-        self.loader = getattr(module, '__loader__', None)
-        self.module_path = os.path.dirname(getattr(module, '__file__', ''))
-
-    def get_resource_filename(self, manager, resource_name):
-        return self._fn(self.module_path, resource_name)
-
-    def get_resource_stream(self, manager, resource_name):
-        return StringIO(self.get_resource_string(manager, resource_name))
-
-    def get_resource_string(self, manager, resource_name):
-        return self._get(self._fn(self.module_path, resource_name))
-
-    def has_resource(self, resource_name):
-        return self._has(self._fn(self.module_path, resource_name))
-
-    def has_metadata(self, name):
-        return self.egg_info and self._has(self._fn(self.egg_info,name))
-
-    if sys.version_info <= (3,):
-        def get_metadata(self, name):
-            if not self.egg_info:
-                return ""
-            return self._get(self._fn(self.egg_info,name))
-    else:
-        def get_metadata(self, name):
-            if not self.egg_info:
-                return ""
-            return self._get(self._fn(self.egg_info,name)).decode("utf-8")
-
-    def get_metadata_lines(self, name):
-        return yield_lines(self.get_metadata(name))
-
-    def resource_isdir(self,resource_name):
-        return self._isdir(self._fn(self.module_path, resource_name))
-
-    def metadata_isdir(self,name):
-        return self.egg_info and self._isdir(self._fn(self.egg_info,name))
-
-
-    def resource_listdir(self,resource_name):
-        return self._listdir(self._fn(self.module_path,resource_name))
-
-    def metadata_listdir(self,name):
-        if self.egg_info:
-            return self._listdir(self._fn(self.egg_info,name))
-        return []
-
-    def run_script(self,script_name,namespace):
-        script = 'scripts/'+script_name
-        if not self.has_metadata(script):
-            raise ResolutionError("No script named %r" % script_name)
-        script_text = self.get_metadata(script).replace('\r\n','\n')
-        script_text = script_text.replace('\r','\n')
-        script_filename = self._fn(self.egg_info,script)
-        namespace['__file__'] = script_filename
-        if os.path.exists(script_filename):
-            execfile(script_filename, namespace, namespace)
-        else:
-            from linecache import cache
-            cache[script_filename] = (
-                len(script_text), 0, script_text.split('\n'), script_filename
-            )
-            script_code = compile(script_text,script_filename,'exec')
-            exec script_code in namespace, namespace
-
-    def _has(self, path):
-        raise NotImplementedError(
-            "Can't perform this operation for unregistered loader type"
-        )
-
-    def _isdir(self, path):
-        raise NotImplementedError(
-            "Can't perform this operation for unregistered loader type"
-        )
-
-    def _listdir(self, path):
-        raise NotImplementedError(
-            "Can't perform this operation for unregistered loader type"
-        )
-
-    def _fn(self, base, resource_name):
-        if resource_name:
-            return os.path.join(base, *resource_name.split('/'))
-        return base
-
-    def _get(self, path):
-        if hasattr(self.loader, 'get_data'):
-            return self.loader.get_data(path)
-        raise NotImplementedError(
-            "Can't perform this operation for loaders without 'get_data()'"
-        )
-
-register_loader_type(object, NullProvider)
-
-
-class EggProvider(NullProvider):
-    """Provider based on a virtual filesystem"""
-
-    def __init__(self,module):
-        NullProvider.__init__(self,module)
-        self._setup_prefix()
-
-    def _setup_prefix(self):
-        # we assume here that our metadata may be nested inside a "basket"
-        # of multiple eggs; that's why we use module_path instead of .archive
-        path = self.module_path
-        old = None
-        while path!=old:
-            if path.lower().endswith('.egg'):
-                self.egg_name = os.path.basename(path)
-                self.egg_info = os.path.join(path, 'EGG-INFO')
-                self.egg_root = path
-                break
-            old = path
-            path, base = os.path.split(path)
-
-
-
-
-
-
-class DefaultProvider(EggProvider):
-    """Provides access to package resources in the filesystem"""
-
-    def _has(self, path):
-        return os.path.exists(path)
-
-    def _isdir(self,path):
-        return os.path.isdir(path)
-
-    def _listdir(self,path):
-        return os.listdir(path)
-
-    def get_resource_stream(self, manager, resource_name):
-        return open(self._fn(self.module_path, resource_name), 'rb')
-
-    def _get(self, path):
-        stream = open(path, 'rb')
-        try:
-            return stream.read()
-        finally:
-            stream.close()
-
-register_loader_type(type(None), DefaultProvider)
-
-
-class EmptyProvider(NullProvider):
-    """Provider that returns nothing for all requests"""
-
-    _isdir = _has = lambda self,path: False
-    _get          = lambda self,path: ''
-    _listdir      = lambda self,path: []
-    module_path   = None
-
-    def __init__(self):
-        pass
-
-empty_provider = EmptyProvider()
-
-
-
-
-class ZipProvider(EggProvider):
-    """Resource support for zips and eggs"""
-
-    eagers = None
-
-    def __init__(self, module):
-        EggProvider.__init__(self,module)
-        self.zipinfo = zipimport._zip_directory_cache[self.loader.archive]
-        self.zip_pre = self.loader.archive+os.sep
-
-    def _zipinfo_name(self, fspath):
-        # Convert a virtual filename (full path to file) into a zipfile subpath
-        # usable with the zipimport directory cache for our target archive
-        if fspath.startswith(self.zip_pre):
-            return fspath[len(self.zip_pre):]
-        raise AssertionError(
-            "%s is not a subpath of %s" % (fspath,self.zip_pre)
-        )
-
-    def _parts(self,zip_path):
-        # Convert a zipfile subpath into an egg-relative path part list
-        fspath = self.zip_pre+zip_path  # pseudo-fs path
-        if fspath.startswith(self.egg_root+os.sep):
-            return fspath[len(self.egg_root)+1:].split(os.sep)
-        raise AssertionError(
-            "%s is not a subpath of %s" % (fspath,self.egg_root)
-        )
-
-    def get_resource_filename(self, manager, resource_name):
-        if not self.egg_name:
-            raise NotImplementedError(
-                "resource_filename() only supported for .egg, not .zip"
-            )
-        # no need to lock for extraction, since we use temp names
-        zip_path = self._resource_to_zip(resource_name)
-        eagers = self._get_eager_resources()
-        if '/'.join(self._parts(zip_path)) in eagers:
-            for name in eagers:
-                self._extract_resource(manager, self._eager_to_zip(name))
-        return self._extract_resource(manager, zip_path)
-
-    def _extract_resource(self, manager, zip_path):
-
-        if zip_path in self._index():
-            for name in self._index()[zip_path]:
-                last = self._extract_resource(
-                    manager, os.path.join(zip_path, name)
-                )
-            return os.path.dirname(last)  # return the extracted directory name
-
-        zip_stat = self.zipinfo[zip_path]
-        t,d,size = zip_stat[5], zip_stat[6], zip_stat[3]
-        date_time = (
-            (d>>9)+1980, (d>>5)&0xF, d&0x1F,                      # ymd
-            (t&0xFFFF)>>11, (t>>5)&0x3F, (t&0x1F) * 2, 0, 0, -1   # hms, etc.
-        )
-        timestamp = time.mktime(date_time)
-
-        try:
-            if not WRITE_SUPPORT:
-                raise IOError('"os.rename" and "os.unlink" are not supported '
-                              'on this platform')
-
-            real_path = manager.get_cache_path(
-                self.egg_name, self._parts(zip_path)
-            )
-
-            if os.path.isfile(real_path):
-                stat = os.stat(real_path)
-                if stat.st_size==size and stat.st_mtime==timestamp:
-                    # size and stamp match, don't bother extracting
-                    return real_path
-
-            outf, tmpnam = _mkstemp(".$extract", dir=os.path.dirname(real_path))
-            os.write(outf, self.loader.get_data(zip_path))
-            os.close(outf)
-            utime(tmpnam, (timestamp,timestamp))
-            manager.postprocess(tmpnam, real_path)
-
-            try:
-                rename(tmpnam, real_path)
-
-            except os.error:
-                if os.path.isfile(real_path):
-                    stat = os.stat(real_path)
-
-                    if stat.st_size==size and stat.st_mtime==timestamp:
-                        # size and stamp match, somebody did it just ahead of
-                        # us, so we're done
-                        return real_path
-                    elif os.name=='nt':     # Windows, del old file and retry
-                        unlink(real_path)
-                        rename(tmpnam, real_path)
-                        return real_path
-                raise
-
-        except os.error:
-            manager.extraction_error()  # report a user-friendly error
-
-        return real_path
-
-    def _get_eager_resources(self):
-        if self.eagers is None:
-            eagers = []
-            for name in ('native_libs.txt', 'eager_resources.txt'):
-                if self.has_metadata(name):
-                    eagers.extend(self.get_metadata_lines(name))
-            self.eagers = eagers
-        return self.eagers
-
-    def _index(self):
-        try:
-            return self._dirindex
-        except AttributeError:
-            ind = {}
-            for path in self.zipinfo:
-                parts = path.split(os.sep)
-                while parts:
-                    parent = os.sep.join(parts[:-1])
-                    if parent in ind:
-                        ind[parent].append(parts[-1])
-                        break
-                    else:
-                        ind[parent] = [parts.pop()]
-            self._dirindex = ind
-            return ind
-
-    def _has(self, fspath):
-        zip_path = self._zipinfo_name(fspath)
-        return zip_path in self.zipinfo or zip_path in self._index()
-
-    def _isdir(self,fspath):
-        return self._zipinfo_name(fspath) in self._index()
-
-    def _listdir(self,fspath):
-        return list(self._index().get(self._zipinfo_name(fspath), ()))
-
-    def _eager_to_zip(self,resource_name):
-        return self._zipinfo_name(self._fn(self.egg_root,resource_name))
-
-    def _resource_to_zip(self,resource_name):
-        return self._zipinfo_name(self._fn(self.module_path,resource_name))
-
-register_loader_type(zipimport.zipimporter, ZipProvider)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-class FileMetadata(EmptyProvider):
-    """Metadata handler for standalone PKG-INFO files
-
-    Usage::
-
-        metadata = FileMetadata("/path/to/PKG-INFO")
-
-    This provider rejects all data and metadata requests except for PKG-INFO,
-    which is treated as existing, and will be the contents of the file at
-    the provided location.
-    """
-
-    def __init__(self,path):
-        self.path = path
-
-    def has_metadata(self,name):
-        return name=='PKG-INFO'
-
-    def get_metadata(self,name):
-        if name=='PKG-INFO':
-            f = open(self.path,'rU')
-            metadata = f.read()
-            f.close()
-            return metadata
-        raise KeyError("No metadata except PKG-INFO is available")
-
-    def get_metadata_lines(self,name):
-        return yield_lines(self.get_metadata(name))
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-class PathMetadata(DefaultProvider):
-    """Metadata provider for egg directories
-
-    Usage::
-
-        # Development eggs:
-
-        egg_info = "/path/to/PackageName.egg-info"
-        base_dir = os.path.dirname(egg_info)
-        metadata = PathMetadata(base_dir, egg_info)
-        dist_name = os.path.splitext(os.path.basename(egg_info))[0]
-        dist = Distribution(basedir,project_name=dist_name,metadata=metadata)
-
-        # Unpacked egg directories:
-
-        egg_path = "/path/to/PackageName-ver-pyver-etc.egg"
-        metadata = PathMetadata(egg_path, os.path.join(egg_path,'EGG-INFO'))
-        dist = Distribution.from_filename(egg_path, metadata=metadata)
-    """
-
-    def __init__(self, path, egg_info):
-        self.module_path = path
-        self.egg_info = egg_info
-
-
-class EggMetadata(ZipProvider):
-    """Metadata provider for .egg files"""
-
-    def __init__(self, importer):
-        """Create a metadata provider from a zipimporter"""
-
-        self.zipinfo = zipimport._zip_directory_cache[importer.archive]
-        self.zip_pre = importer.archive+os.sep
-        self.loader = importer
-        if importer.prefix:
-            self.module_path = os.path.join(importer.archive, importer.prefix)
-        else:
-            self.module_path = importer.archive
-        self._setup_prefix()
-
-
-class ImpWrapper:
-    """PEP 302 Importer that wraps Python's "normal" import algorithm"""
-
-    def __init__(self, path=None):
-        self.path = path
-
-    def find_module(self, fullname, path=None):
-        subname = fullname.split(".")[-1]
-        if subname != fullname and self.path is None:
-            return None
-        if self.path is None:
-            path = None
-        else:
-            path = [self.path]
-        try:
-            file, filename, etc = imp.find_module(subname, path)
-        except ImportError:
-            return None
-        return ImpLoader(file, filename, etc)
-
-
-class ImpLoader:
-    """PEP 302 Loader that wraps Python's "normal" import algorithm"""
-
-    def __init__(self, file, filename, etc):
-        self.file = file
-        self.filename = filename
-        self.etc = etc
-
-    def load_module(self, fullname):
-        try:
-            mod = imp.load_module(fullname, self.file, self.filename, self.etc)
-        finally:
-            if self.file: self.file.close()
-        # Note: we don't set __loader__ because we want the module to look
-        # normal; i.e. this is just a wrapper for standard import machinery
-        return mod
-
-
-
-
-def get_importer(path_item):
-    """Retrieve a PEP 302 "importer" for the given path item
-
-    If there is no importer, this returns a wrapper around the builtin import
-    machinery.  The returned importer is only cached if it was created by a
-    path hook.
-    """
-    try:
-        importer = sys.path_importer_cache[path_item]
-    except KeyError:
-        for hook in sys.path_hooks:
-            try:
-                importer = hook(path_item)
-            except ImportError:
-                pass
-            else:
-                break
-        else:
-            importer = None
-
-    sys.path_importer_cache.setdefault(path_item,importer)
-    if importer is None:
-        try:
-            importer = ImpWrapper(path_item)
-        except ImportError:
-            pass
-    return importer
-
-try:
-    from pkgutil import get_importer, ImpImporter
-except ImportError:
-    pass    # Python 2.3 or 2.4, use our own implementation
-else:
-    ImpWrapper = ImpImporter    # Python 2.5, use pkgutil's implementation
-    del ImpLoader, ImpImporter
-
-
-
-
-
-
-_distribution_finders = {}
-
-def register_finder(importer_type, distribution_finder):
-    """Register `distribution_finder` to find distributions in sys.path items
-
-    `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item
-    handler), and `distribution_finder` is a callable that, passed a path
-    item and the importer instance, yields ``Distribution`` instances found on
-    that path item.  See ``pkg_resources.find_on_path`` for an example."""
-    _distribution_finders[importer_type] = distribution_finder
-
-
-def find_distributions(path_item, only=False):
-    """Yield distributions accessible via `path_item`"""
-    importer = get_importer(path_item)
-    finder = _find_adapter(_distribution_finders, importer)
-    return finder(importer, path_item, only)
-
-def find_in_zip(importer, path_item, only=False):
-    metadata = EggMetadata(importer)
-    if metadata.has_metadata('PKG-INFO'):
-        yield Distribution.from_filename(path_item, metadata=metadata)
-    if only:
-        return  # don't yield nested distros
-    for subitem in metadata.resource_listdir('/'):
-        if subitem.endswith('.egg'):
-            subpath = os.path.join(path_item, subitem)
-            for dist in find_in_zip(zipimport.zipimporter(subpath), subpath):
-                yield dist
-
-register_finder(zipimport.zipimporter, find_in_zip)
-
-def StringIO(*args, **kw):
-    """Thunk to load the real StringIO on demand"""
-    global StringIO
-    try:
-        from cStringIO import StringIO
-    except ImportError:
-        from StringIO import StringIO
-    return StringIO(*args,**kw)
-
-def find_nothing(importer, path_item, only=False):
-    return ()
-register_finder(object,find_nothing)
-
-def find_on_path(importer, path_item, only=False):
-    """Yield distributions accessible on a sys.path directory"""
-    path_item = _normalize_cached(path_item)
-
-    if os.path.isdir(path_item) and os.access(path_item, os.R_OK):
-        if path_item.lower().endswith('.egg'):
-            # unpacked egg
-            yield Distribution.from_filename(
-                path_item, metadata=PathMetadata(
-                    path_item, os.path.join(path_item,'EGG-INFO')
-                )
-            )
-        else:
-            # scan for .egg and .egg-info in directory
-            for entry in os.listdir(path_item):
-                lower = entry.lower()
-                if lower.endswith('.egg-info'):
-                    fullpath = os.path.join(path_item, entry)
-                    if os.path.isdir(fullpath):
-                        # egg-info directory, allow getting metadata
-                        metadata = PathMetadata(path_item, fullpath)
-                    else:
-                        metadata = FileMetadata(fullpath)
-                    yield Distribution.from_location(
-                        path_item,entry,metadata,precedence=DEVELOP_DIST
-                    )
-                elif not only and lower.endswith('.egg'):
-                    for dist in find_distributions(os.path.join(path_item, entry)):
-                        yield dist
-                elif not only and lower.endswith('.egg-link'):
-                    for line in open(os.path.join(path_item, entry)):
-                        if not line.strip(): continue
-                        for item in find_distributions(os.path.join(path_item,line.rstrip())):
-                            yield item
-                        break
-register_finder(ImpWrapper,find_on_path)
-
-_namespace_handlers = {}
-_namespace_packages = {}
-
-def register_namespace_handler(importer_type, namespace_handler):
-    """Register `namespace_handler` to declare namespace packages
-
-    `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item
-    handler), and `namespace_handler` is a callable like this::
-
-        def namespace_handler(importer,path_entry,moduleName,module):
-            # return a path_entry to use for child packages
-
-    Namespace handlers are only called if the importer object has already
-    agreed that it can handle the relevant path item, and they should only
-    return a subpath if the module __path__ does not already contain an
-    equivalent subpath.  For an example namespace handler, see
-    ``pkg_resources.file_ns_handler``.
-    """
-    _namespace_handlers[importer_type] = namespace_handler
-
-def _handle_ns(packageName, path_item):
-    """Ensure that named package includes a subpath of path_item (if needed)"""
-    importer = get_importer(path_item)
-    if importer is None:
-        return None
-    loader = importer.find_module(packageName)
-    if loader is None:
-        return None
-    module = sys.modules.get(packageName)
-    if module is None:
-        module = sys.modules[packageName] = types.ModuleType(packageName)
-        module.__path__ = []; _set_parent_ns(packageName)
-    elif not hasattr(module,'__path__'):
-        raise TypeError("Not a package:", packageName)
-    handler = _find_adapter(_namespace_handlers, importer)
-    subpath = handler(importer,path_item,packageName,module)
-    if subpath is not None:
-        path = module.__path__; path.append(subpath)
-        loader.load_module(packageName); module.__path__ = path
-    return subpath
-
-def declare_namespace(packageName):
-    """Declare that package 'packageName' is a namespace package"""
-
-    imp.acquire_lock()
-    try:
-        if packageName in _namespace_packages:
-            return
-
-        path, parent = sys.path, None
-        if '.' in packageName:
-            parent = '.'.join(packageName.split('.')[:-1])
-            declare_namespace(parent)
-            __import__(parent)
-            try:
-                path = sys.modules[parent].__path__
-            except AttributeError:
-                raise TypeError("Not a package:", parent)
-
-        # Track what packages are namespaces, so when new path items are added,
-        # they can be updated
-        _namespace_packages.setdefault(parent,[]).append(packageName)
-        _namespace_packages.setdefault(packageName,[])
-
-        for path_item in path:
-            # Ensure all the parent's path items are reflected in the child,
-            # if they apply
-            _handle_ns(packageName, path_item)
-
-    finally:
-        imp.release_lock()
-
-def fixup_namespace_packages(path_item, parent=None):
-    """Ensure that previously-declared namespace packages include path_item"""
-    imp.acquire_lock()
-    try:
-        for package in _namespace_packages.get(parent,()):
-            subpath = _handle_ns(package, path_item)
-            if subpath: fixup_namespace_packages(subpath,package)
-    finally:
-        imp.release_lock()
-
-def file_ns_handler(importer, path_item, packageName, module):
-    """Compute an ns-package subpath for a filesystem or zipfile importer"""
-
-    subpath = os.path.join(path_item, packageName.split('.')[-1])
-    normalized = _normalize_cached(subpath)
-    for item in module.__path__:
-        if _normalize_cached(item)==normalized:
-            break
-    else:
-        # Only return the path if it's not already there
-        return subpath
-
-register_namespace_handler(ImpWrapper,file_ns_handler)
-register_namespace_handler(zipimport.zipimporter,file_ns_handler)
-
-
-def null_ns_handler(importer, path_item, packageName, module):
-    return None
-
-register_namespace_handler(object,null_ns_handler)
-
-
-def normalize_path(filename):
-    """Normalize a file/dir name for comparison purposes"""
-    return os.path.normcase(os.path.realpath(filename))
-
-def _normalize_cached(filename,_cache={}):
-    try:
-        return _cache[filename]
-    except KeyError:
-        _cache[filename] = result = normalize_path(filename)
-        return result
-
-def _set_parent_ns(packageName):
-    parts = packageName.split('.')
-    name = parts.pop()
-    if parts:
-        parent = '.'.join(parts)
-        setattr(sys.modules[parent], name, sys.modules[packageName])
-
-
-def yield_lines(strs):
-    """Yield non-empty/non-comment lines of a ``basestring`` or sequence"""
-    if isinstance(strs,basestring):
-        for s in strs.splitlines():
-            s = s.strip()
-            if s and not s.startswith('#'):     # skip blank lines/comments
-                yield s
-    else:
-        for ss in strs:
-            for s in yield_lines(ss):
-                yield s
-
-LINE_END = re.compile(r"\s*(#.*)?$").match         # whitespace and comment
-CONTINUE = re.compile(r"\s*\\\s*(#.*)?$").match    # line continuation
-DISTRO   = re.compile(r"\s*((\w|[-.])+)").match    # Distribution or extra
-VERSION  = re.compile(r"\s*(<=?|>=?|==|!=)\s*((\w|[-.])+)").match  # ver. info
-COMMA    = re.compile(r"\s*,").match               # comma between items
-OBRACKET = re.compile(r"\s*\[").match
-CBRACKET = re.compile(r"\s*\]").match
-MODULE   = re.compile(r"\w+(\.\w+)*$").match
-EGG_NAME = re.compile(
-    r"(?P<name>[^-]+)"
-    r"( -(?P<ver>[^-]+) (-py(?P<pyver>[^-]+) (-(?P<plat>.+))? )? )?",
-    re.VERBOSE | re.IGNORECASE
-).match
-
-component_re = re.compile(r'(\d+ | [a-z]+ | \.| -)', re.VERBOSE)
-replace = {'pre':'c', 'preview':'c','-':'final-','rc':'c','dev':'@'}.get
-
-def _parse_version_parts(s):
-    for part in component_re.split(s):
-        part = replace(part,part)
-        if not part or part=='.':
-            continue
-        if part[:1] in '0123456789':
-            yield part.zfill(8)    # pad for numeric comparison
-        else:
-            yield '*'+part
-
-    yield '*final'  # ensure that alpha/beta/candidate are before final
-
-def parse_version(s):
-    """Convert a version string to a chronologically-sortable key
-
-    This is a rough cross between distutils' StrictVersion and LooseVersion;
-    if you give it versions that would work with StrictVersion, then it behaves
-    the same; otherwise it acts like a slightly-smarter LooseVersion. It is
-    *possible* to create pathological version coding schemes that will fool
-    this parser, but they should be very rare in practice.
-
-    The returned value will be a tuple of strings.  Numeric portions of the
-    version are padded to 8 digits so they will compare numerically, but
-    without relying on how numbers compare relative to strings.  Dots are
-    dropped, but dashes are retained.  Trailing zeros between alpha segments
-    or dashes are suppressed, so that e.g. "2.4.0" is considered the same as
-    "2.4". Alphanumeric parts are lower-cased.
-
-    The algorithm assumes that strings like "-" and any alpha string that
-    alphabetically follows "final"  represents a "patch level".  So, "2.4-1"
-    is assumed to be a branch or patch of "2.4", and therefore "2.4.1" is
-    considered newer than "2.4-1", which in turn is newer than "2.4".
-
-    Strings like "a", "b", "c", "alpha", "beta", "candidate" and so on (that
-    come before "final" alphabetically) are assumed to be pre-release versions,
-    so that the version "2.4" is considered newer than "2.4a1".
-
-    Finally, to handle miscellaneous cases, the strings "pre", "preview", and
-    "rc" are treated as if they were "c", i.e. as though they were release
-    candidates, and therefore are not as new as a version string that does not
-    contain them, and "dev" is replaced with an '@' so that it sorts lower than
-    than any other pre-release tag.
-    """
-    parts = []
-    for part in _parse_version_parts(s.lower()):
-        if part.startswith('*'):
-            if part<'*final':   # remove '-' before a prerelease tag
-                while parts and parts[-1]=='*final-': parts.pop()
-            # remove trailing zeros from each series of numeric parts
-            while parts and parts[-1]=='00000000':
-                parts.pop()
-        parts.append(part)
-    return tuple(parts)
-
-class EntryPoint(object):
-    """Object representing an advertised importable object"""
-
-    def __init__(self, name, module_name, attrs=(), extras=(), dist=None):
-        if not MODULE(module_name):
-            raise ValueError("Invalid module name", module_name)
-        self.name = name
-        self.module_name = module_name
-        self.attrs = tuple(attrs)
-        self.extras = Requirement.parse(("x[%s]" % ','.join(extras))).extras
-        self.dist = dist
-
-    def __str__(self):
-        s = "%s = %s" % (self.name, self.module_name)
-        if self.attrs:
-            s += ':' + '.'.join(self.attrs)
-        if self.extras:
-            s += ' [%s]' % ','.join(self.extras)
-        return s
-
-    def __repr__(self):
-        return "EntryPoint.parse(%r)" % str(self)
-
-    def load(self, require=True, env=None, installer=None):
-        if require: self.require(env, installer)
-        entry = __import__(self.module_name, globals(),globals(), ['__name__'])
-        for attr in self.attrs:
-            try:
-                entry = getattr(entry,attr)
-            except AttributeError:
-                raise ImportError("%r has no %r attribute" % (entry,attr))
-        return entry
-
-    def require(self, env=None, installer=None):
-        if self.extras and not self.dist:
-            raise UnknownExtra("Can't require() without a distribution", self)
-        map(working_set.add,
-            working_set.resolve(self.dist.requires(self.extras),env,installer))
-
-
-
-    #@classmethod
-    def parse(cls, src, dist=None):
-        """Parse a single entry point from string `src`
-
-        Entry point syntax follows the form::
-
-            name = some.module:some.attr [extra1,extra2]
-
-        The entry name and module name are required, but the ``:attrs`` and
-        ``[extras]`` parts are optional
-        """
-        try:
-            attrs = extras = ()
-            name,value = src.split('=',1)
-            if '[' in value:
-                value,extras = value.split('[',1)
-                req = Requirement.parse("x["+extras)
-                if req.specs: raise ValueError
-                extras = req.extras
-            if ':' in value:
-                value,attrs = value.split(':',1)
-                if not MODULE(attrs.rstrip()):
-                    raise ValueError
-                attrs = attrs.rstrip().split('.')
-        except ValueError:
-            raise ValueError(
-                "EntryPoint must be in 'name=module:attrs [extras]' format",
-                src
-            )
-        else:
-            return cls(name.strip(), value.strip(), attrs, extras, dist)
-
-    parse = classmethod(parse)
-
-
-
-
-
-
-
-
-    #@classmethod
-    def parse_group(cls, group, lines, dist=None):
-        """Parse an entry point group"""
-        if not MODULE(group):
-            raise ValueError("Invalid group name", group)
-        this = {}
-        for line in yield_lines(lines):
-            ep = cls.parse(line, dist)
-            if ep.name in this:
-                raise ValueError("Duplicate entry point", group, ep.name)
-            this[ep.name]=ep
-        return this
-
-    parse_group = classmethod(parse_group)
-
-    #@classmethod
-    def parse_map(cls, data, dist=None):
-        """Parse a map of entry point groups"""
-        if isinstance(data,dict):
-            data = data.items()
-        else:
-            data = split_sections(data)
-        maps = {}
-        for group, lines in data:
-            if group is None:
-                if not lines:
-                    continue
-                raise ValueError("Entry points must be listed in groups")
-            group = group.strip()
-            if group in maps:
-                raise ValueError("Duplicate group name", group)
-            maps[group] = cls.parse_group(group, lines, dist)
-        return maps
-
-    parse_map = classmethod(parse_map)
-
-
-def _remove_md5_fragment(location):
-    if not location:
-        return ''
-    parsed = urlparse(location)
-    if parsed[-1].startswith('md5='):
-        return urlunparse(parsed[:-1] + ('',))
-    return location
-
-
-class Distribution(object):
-    """Wrap an actual or potential sys.path entry w/metadata"""
-    def __init__(self,
-        location=None, metadata=None, project_name=None, version=None,
-        py_version=PY_MAJOR, platform=None, precedence = EGG_DIST
-    ):
-        self.project_name = safe_name(project_name or 'Unknown')
-        if version is not None:
-            self._version = safe_version(version)
-        self.py_version = py_version
-        self.platform = platform
-        self.location = location
-        self.precedence = precedence
-        self._provider = metadata or empty_provider
-
-    #@classmethod
-    def from_location(cls,location,basename,metadata=None,**kw):
-        project_name, version, py_version, platform = [None]*4
-        basename, ext = os.path.splitext(basename)
-        if ext.lower() in (".egg",".egg-info"):
-            match = EGG_NAME(basename)
-            if match:
-                project_name, version, py_version, platform = match.group(
-                    'name','ver','pyver','plat'
-                )
-        return cls(
-            location, metadata, project_name=project_name, version=version,
-            py_version=py_version, platform=platform, **kw
-        )
-    from_location = classmethod(from_location)
-
-
-    hashcmp = property(
-        lambda self: (
-            getattr(self,'parsed_version',()),
-            self.precedence,
-            self.key,
-            _remove_md5_fragment(self.location),
-            self.py_version,
-            self.platform
-        )
-    )
-    def __hash__(self): return hash(self.hashcmp)
-    def __lt__(self, other):
-        return self.hashcmp < other.hashcmp
-    def __le__(self, other):
-        return self.hashcmp <= other.hashcmp
-    def __gt__(self, other):
-        return self.hashcmp > other.hashcmp
-    def __ge__(self, other):
-        return self.hashcmp >= other.hashcmp
-    def __eq__(self, other):
-        if not isinstance(other, self.__class__):
-            # It's not a Distribution, so they are not equal
-            return False
-        return self.hashcmp == other.hashcmp
-    def __ne__(self, other):
-        return not self == other
-
-    # These properties have to be lazy so that we don't have to load any
-    # metadata until/unless it's actually needed.  (i.e., some distributions
-    # may not know their name or version without loading PKG-INFO)
-
-    #@property
-    def key(self):
-        try:
-            return self._key
-        except AttributeError:
-            self._key = key = self.project_name.lower()
-            return key
-    key = property(key)
-
-    #@property
-    def parsed_version(self):
-        try:
-            return self._parsed_version
-        except AttributeError:
-            self._parsed_version = pv = parse_version(self.version)
-            return pv
-
-    parsed_version = property(parsed_version)
-
-    #@property
-    def version(self):
-        try:
-            return self._version
-        except AttributeError:
-            for line in self._get_metadata('PKG-INFO'):
-                if line.lower().startswith('version:'):
-                    self._version = safe_version(line.split(':',1)[1].strip())
-                    return self._version
-            else:
-                raise ValueError(
-                    "Missing 'Version:' header and/or PKG-INFO file", self
-                )
-    version = property(version)
-
-
-
-
-    #@property
-    def _dep_map(self):
-        try:
-            return self.__dep_map
-        except AttributeError:
-            dm = self.__dep_map = {None: []}
-            for name in 'requires.txt', 'depends.txt':
-                for extra,reqs in split_sections(self._get_metadata(name)):
-                    if extra: extra = safe_extra(extra)
-                    dm.setdefault(extra,[]).extend(parse_requirements(reqs))
-            return dm
-    _dep_map = property(_dep_map)
-
-    def requires(self,extras=()):
-        """List of Requirements needed for this distro if `extras` are used"""
-        dm = self._dep_map
-        deps = []
-        deps.extend(dm.get(None,()))
-        for ext in extras:
-            try:
-                deps.extend(dm[safe_extra(ext)])
-            except KeyError:
-                raise UnknownExtra(
-                    "%s has no such extra feature %r" % (self, ext)
-                )
-        return deps
-
-    def _get_metadata(self,name):
-        if self.has_metadata(name):
-            for line in self.get_metadata_lines(name):
-                yield line
-
-    def activate(self,path=None):
-        """Ensure distribution is importable on `path` (default=sys.path)"""
-        if path is None: path = sys.path
-        self.insert_on(path)
-        if path is sys.path:
-            fixup_namespace_packages(self.location)
-            map(declare_namespace, self._get_metadata('namespace_packages.txt'))
-
-
-    def egg_name(self):
-        """Return what this distribution's standard .egg filename should be"""
-        filename = "%s-%s-py%s" % (
-            to_filename(self.project_name), to_filename(self.version),
-            self.py_version or PY_MAJOR
-        )
-
-        if self.platform:
-            filename += '-'+self.platform
-        return filename
-
-    def __repr__(self):
-        if self.location:
-            return "%s (%s)" % (self,self.location)
-        else:
-            return str(self)
-
-    def __str__(self):
-        try: version = getattr(self,'version',None)
-        except ValueError: version = None
-        version = version or "[unknown version]"
-        return "%s %s" % (self.project_name,version)
-
-    def __getattr__(self,attr):
-        """Delegate all unrecognized public attributes to .metadata provider"""
-        if attr.startswith('_'):
-            raise AttributeError,attr
-        return getattr(self._provider, attr)
-
-    #@classmethod
-    def from_filename(cls,filename,metadata=None, **kw):
-        return cls.from_location(
-            _normalize_cached(filename), os.path.basename(filename), metadata,
-            **kw
-        )
-    from_filename = classmethod(from_filename)
-
-    def as_requirement(self):
-        """Return a ``Requirement`` that matches this distribution exactly"""
-        return Requirement.parse('%s==%s' % (self.project_name, self.version))
-
-    def load_entry_point(self, group, name):
-        """Return the `name` entry point of `group` or raise ImportError"""
-        ep = self.get_entry_info(group,name)
-        if ep is None:
-            raise ImportError("Entry point %r not found" % ((group,name),))
-        return ep.load()
-
-    def get_entry_map(self, group=None):
-        """Return the entry point map for `group`, or the full entry map"""
-        try:
-            ep_map = self._ep_map
-        except AttributeError:
-            ep_map = self._ep_map = EntryPoint.parse_map(
-                self._get_metadata('entry_points.txt'), self
-            )
-        if group is not None:
-            return ep_map.get(group,{})
-        return ep_map
-
-    def get_entry_info(self, group, name):
-        """Return the EntryPoint object for `group`+`name`, or ``None``"""
-        return self.get_entry_map(group).get(name)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-    def insert_on(self, path, loc = None):
-        """Insert self.location in path before its nearest parent directory"""
-
-        loc = loc or self.location
-
-        if self.project_name == 'setuptools':
-            try:
-                version = self.version
-            except ValueError:
-                version = ''
-            if '0.7' in version:
-                raise ValueError(
-                    "A 0.7-series setuptools cannot be installed "
-                    "with distribute. Found one at %s" % str(self.location))
-
-        if not loc:
-            return
-
-        if path is sys.path:
-            self.check_version_conflict()
-
-        nloc = _normalize_cached(loc)
-        bdir = os.path.dirname(nloc)
-        npath= map(_normalize_cached, path)
-
-        bp = None
-        for p, item in enumerate(npath):
-            if item==nloc:
-                break
-            elif item==bdir and self.precedence==EGG_DIST:
-                # if it's an .egg, give it precedence over its directory
-                path.insert(p, loc)
-                npath.insert(p, nloc)
-                break
-        else:
-            path.append(loc)
-            return
-
-        # p is the spot where we found or inserted loc; now remove duplicates
-        while 1:
-            try:
-                np = npath.index(nloc, p+1)
-            except ValueError:
-                break
-            else:
-                del npath[np], path[np]
-                p = np  # ha!
-
-        return
-
-
-
-    def check_version_conflict(self):
-        if self.key=='distribute':
-            return      # ignore the inevitable setuptools self-conflicts  :(
-
-        nsp = dict.fromkeys(self._get_metadata('namespace_packages.txt'))
-        loc = normalize_path(self.location)
-        for modname in self._get_metadata('top_level.txt'):
-            if (modname not in sys.modules or modname in nsp
-                or modname in _namespace_packages
-            ):
-                continue
-            if modname in ('pkg_resources', 'setuptools', 'site'):
-                continue
-            fn = getattr(sys.modules[modname], '__file__', None)
-            if fn and (normalize_path(fn).startswith(loc) or
-                       fn.startswith(self.location)):
-                continue
-            issue_warning(
-                "Module %s was already imported from %s, but %s is being added"
-                " to sys.path" % (modname, fn, self.location),
-            )
-
-    def has_version(self):
-        try:
-            self.version
-        except ValueError:
-            issue_warning("Unbuilt egg for "+repr(self))
-            return False
-        return True
-
-    def clone(self,**kw):
-        """Copy this distribution, substituting in any changed keyword args"""
-        for attr in (
-            'project_name', 'version', 'py_version', 'platform', 'location',
-            'precedence'
-        ):
-            kw.setdefault(attr, getattr(self,attr,None))
-        kw.setdefault('metadata', self._provider)
-        return self.__class__(**kw)
-
-
-
-
-    #@property
-    def extras(self):
-        return [dep for dep in self._dep_map if dep]
-    extras = property(extras)
-
-
-def issue_warning(*args,**kw):
-    level = 1
-    g = globals()
-    try:
-        # find the first stack frame that is *not* code in
-        # the pkg_resources module, to use for the warning
-        while sys._getframe(level).f_globals is g:
-            level += 1
-    except ValueError:
-        pass
-    from warnings import warn
-    warn(stacklevel = level+1, *args, **kw)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-def parse_requirements(strs):
-    """Yield ``Requirement`` objects for each specification in `strs`
-
-    `strs` must be an instance of ``basestring``, or a (possibly-nested)
-    iterable thereof.
-    """
-    # create a steppable iterator, so we can handle \-continuations
-    lines = iter(yield_lines(strs))
-
-    def scan_list(ITEM,TERMINATOR,line,p,groups,item_name):
-
-        items = []
-
-        while not TERMINATOR(line,p):
-            if CONTINUE(line,p):
-                try:
-                    line = lines.next(); p = 0
-                except StopIteration:
-                    raise ValueError(
-                        "\\ must not appear on the last nonblank line"
-                    )
-
-            match = ITEM(line,p)
-            if not match:
-                raise ValueError("Expected "+item_name+" in",line,"at",line[p:])
-
-            items.append(match.group(*groups))
-            p = match.end()
-
-            match = COMMA(line,p)
-            if match:
-                p = match.end() # skip the comma
-            elif not TERMINATOR(line,p):
-                raise ValueError(
-                    "Expected ',' or end-of-list in",line,"at",line[p:]
-                )
-
-        match = TERMINATOR(line,p)
-        if match: p = match.end()   # skip the terminator, if any
-        return line, p, items
-
-    for line in lines:
-        match = DISTRO(line)
-        if not match:
-            raise ValueError("Missing distribution spec", line)
-        project_name = match.group(1)
-        p = match.end()
-        extras = []
-
-        match = OBRACKET(line,p)
-        if match:
-            p = match.end()
-            line, p, extras = scan_list(
-                DISTRO, CBRACKET, line, p, (1,), "'extra' name"
-            )
-
-        line, p, specs = scan_list(VERSION,LINE_END,line,p,(1,2),"version spec")
-        specs = [(op,safe_version(val)) for op,val in specs]
-        yield Requirement(project_name, specs, extras)
-
-
-def _sort_dists(dists):
-    tmp = [(dist.hashcmp,dist) for dist in dists]
-    tmp.sort()
-    dists[::-1] = [d for hc,d in tmp]
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-class Requirement:
-    def __init__(self, project_name, specs, extras):
-        """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!"""
-        self.unsafe_name, project_name = project_name, safe_name(project_name)
-        self.project_name, self.key = project_name, project_name.lower()
-        index = [(parse_version(v),state_machine[op],op,v) for op,v in specs]
-        index.sort()
-        self.specs = [(op,ver) for parsed,trans,op,ver in index]
-        self.index, self.extras = index, tuple(map(safe_extra,extras))
-        self.hashCmp = (
-            self.key, tuple([(op,parsed) for parsed,trans,op,ver in index]),
-            frozenset(self.extras)
-        )
-        self.__hash = hash(self.hashCmp)
-
-    def __str__(self):
-        specs = ','.join([''.join(s) for s in self.specs])
-        extras = ','.join(self.extras)
-        if extras: extras = '[%s]' % extras
-        return '%s%s%s' % (self.project_name, extras, specs)
-
-    def __eq__(self,other):
-        return isinstance(other,Requirement) and self.hashCmp==other.hashCmp
-
-    def __contains__(self,item):
-        if isinstance(item,Distribution):
-            if item.key <> self.key: return False
-            if self.index: item = item.parsed_version  # only get if we need it
-        elif isinstance(item,basestring):
-            item = parse_version(item)
-        last = None
-        compare = lambda a, b: (a > b) - (a < b) # -1, 0, 1
-        for parsed,trans,op,ver in self.index:
-            action = trans[compare(item,parsed)] # Indexing: 0, 1, -1
-            if action=='F':     return False
-            elif action=='T':   return True
-            elif action=='+':   last = True
-            elif action=='-' or last is None:   last = False
-        if last is None: last = True    # no rules encountered
-        return last
-
-
-    def __hash__(self):
-        return self.__hash
-
-    def __repr__(self): return "Requirement.parse(%r)" % str(self)
-
-    #@staticmethod
-    def parse(s, replacement=True):
-        reqs = list(parse_requirements(s))
-        if reqs:
-            if len(reqs) == 1:
-                founded_req = reqs[0]
-                # if asked for setuptools distribution
-                # and if distribute is installed, we want to give
-                # distribute instead
-                if _override_setuptools(founded_req) and replacement:
-                    distribute = list(parse_requirements('distribute'))
-                    if len(distribute) == 1:
-                        return distribute[0]
-                    return founded_req
-                else:
-                    return founded_req
-
-            raise ValueError("Expected only one requirement", s)
-        raise ValueError("No requirements found", s)
-
-    parse = staticmethod(parse)
-
-state_machine = {
-    #       =><
-    '<' :  '--T',
-    '<=':  'T-T',
-    '>' :  'F+F',
-    '>=':  'T+F',
-    '==':  'T..',
-    '!=':  'F++',
-}
-
-
-def _override_setuptools(req):
-    """Return True when distribute wants to override a setuptools dependency.
-
-    We want to override when the requirement is setuptools and the version is
-    a variant of 0.6.
-
-    """
-    if req.project_name == 'setuptools':
-        if not len(req.specs):
-            # Just setuptools: ok
-            return True
-        for comparator, version in req.specs:
-            if comparator in ['==', '>=', '>']:
-                if '0.7' in version:
-                    # We want some setuptools not from the 0.6 series.
-                    return False
-        return True
-    return False
-
-
-def _get_mro(cls):
-    """Get an mro for a type or classic class"""
-    if not isinstance(cls,type):
-        class cls(cls,object): pass
-        return cls.__mro__[1:]
-    return cls.__mro__
-
-def _find_adapter(registry, ob):
-    """Return an adapter factory for `ob` from `registry`"""
-    for t in _get_mro(getattr(ob, '__class__', type(ob))):
-        if t in registry:
-            return registry[t]
-
-
-def ensure_directory(path):
-    """Ensure that the parent directory of `path` exists"""
-    dirname = os.path.dirname(path)
-    if not os.path.isdir(dirname):
-        os.makedirs(dirname)
-
-def split_sections(s):
-    """Split a string or iterable thereof into (section,content) pairs
-
-    Each ``section`` is a stripped version of the section header ("[section]")
-    and each ``content`` is a list of stripped lines excluding blank lines and
-    comment-only lines.  If there are any such lines before the first section
-    header, they're returned in a first ``section`` of ``None``.
-    """
-    section = None
-    content = []
-    for line in yield_lines(s):
-        if line.startswith("["):
-            if line.endswith("]"):
-                if section or content:
-                    yield section, content
-                section = line[1:-1].strip()
-                content = []
-            else:
-                raise ValueError("Invalid section heading", line)
-        else:
-            content.append(line)
-
-    # wrap up last segment
-    yield section, content
-
-def _mkstemp(*args,**kw):
-    from tempfile import mkstemp
-    old_open = os.open
-    try:
-        os.open = os_open   # temporarily bypass sandboxing
-        return mkstemp(*args,**kw)
-    finally:
-        os.open = old_open  # and then put it back
-
-
-# Set up global resource manager
-_manager = ResourceManager()
-def _initialize(g):
-    for name in dir(_manager):
-        if not name.startswith('_'):
-            g[name] = getattr(_manager, name)
-_initialize(globals())
-
-# Prepare the master working set and make the ``require()`` API available
-working_set = WorkingSet()
-try:
-    # Does the main program list any requirements?
-    from __main__ import __requires__
-except ImportError:
-    pass # No: just use the default working set based on sys.path
-else:
-    # Yes: ensure the requirements are met, by prefixing sys.path if necessary
-    try:
-        working_set.require(__requires__)
-    except VersionConflict:     # try it without defaults already on sys.path
-        working_set = WorkingSet([])    # by starting with an empty path
-        for dist in working_set.resolve(
-            parse_requirements(__requires__), Environment()
-        ):
-            working_set.add(dist)
-        for entry in sys.path:  # add any missing entries from sys.path
-            if entry not in working_set.entries:
-                working_set.add_entry(entry)
-        sys.path[:] = working_set.entries   # then copy back to sys.path
-
-require = working_set.require
-iter_entry_points = working_set.iter_entry_points
-add_activation_listener = working_set.subscribe
-run_script = working_set.run_script
-run_main = run_script   # backward compatibility
-# Activate all distributions already on sys.path, and ensure that
-# all distributions added to the working set in the future (e.g. by
-# calling ``require()``) will get activated as well.
-add_activation_listener(lambda dist: dist.activate())
-working_set.entries=[]; map(working_set.add_entry,sys.path) # match order
-
diff --git a/src/distutils2/pypi/simple.py b/src/distutils2/pypi/simple.py
--- a/src/distutils2/pypi/simple.py
+++ b/src/distutils2/pypi/simple.py
@@ -1,797 +1,393 @@
-"""distutils2.pypi.package_index
+"""pypi.simple
 
-Implement a simple way to use the PyPI API's, by using known URIs from 
-PyPI. Works with the API defined on 
-http://peak.telecommunity.com/DevCenter/EasyInstall#package-index-api , 
-and available at http://pypi.python.org/simple/ .
+Contains the class "SimpleIndex", a simple spider to find and retrieve
+distributions on the Python Package Index, using it's "simple" API,
+avalaible at http://pypi.python.org/simple/
+"""
+from fnmatch import translate
+import httplib
+import re
+import socket
+import sys
+import urllib2
+import urlparse
 
-"""
-# XXX Make a explaination document on how this API is made.
-
-import sys
-import os.path
-import re
-import urlparse
-import urllib2
-import shutil
-import socket
-import cStringIO
-import httplib
-import logging
-
-from distutils2.errors import DistutilsError
+from distutils2.version import VersionPredicate
+from distutils2.pypi.dist import (PyPIDistribution, PyPIDistributions,
+                                  EXTENSIONS)
+from distutils2.pypi.errors import (PyPIError, DistributionNotFound,
+                                    DownloadError, UnableToDownload)
 from distutils2 import __version__ as __distutils2_version__
 
-try:
-    from hashlib import md5
-except ImportError:
-    from md5 import md5
-from fnmatch import translate
+# -- Constants -----------------------------------------------
+PYPI_DEFAULT_INDEX_URL = "http://pypi.python.org/simple/"
+PYPI_DEFAULT_MIRROR_URL = "mirrors.pypi.python.org"
+DEFAULT_HOSTS = ("*",)
+SOCKET_TIMEOUT = 15
+USER_AGENT = "Python-urllib/%s distutils2/%s" % (
+    sys.version[:3], __distutils2_version__)
 
-# -- Constants ----------------------------------------------------------------
-
-__all__ = ['PackageIndex', 
-           'distros_for_url', 
-           'parse_bdist_wininst',
-           'interpret_distro_name',
-]
-
-_SOCKET_TIMEOUT = 15
-SOURCE_DIST = 1
+# -- Regexps -------------------------------------------------
 EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.]+)$')
 HREF = re.compile("""href\\s*=\\s*['"]?([^'"> ]+)""", re.I)
-# this is here to fix emacs' cruddy broken syntax highlighting
 PYPI_MD5 = re.compile(
     '<a href="([^"#]+)">([^<]+)</a>\n\s+\\(<a (?:title="MD5 hash"\n\s+)'
-    'href="[^?]+\?:action=show_md5&amp;digest=([0-9a-f]{32})">md5</a>\\)'
-)
-URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):',re.I).match
-EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz".split()
-
-# -- Dependencies from distribute ---------------------------------------------
-
-# XXX Remove these dependencies, refactor the package_index codebase to not 
-# depends anymore on distribute classes and internal workings.
-from pkg_resources import *
-
-# -- Base functions -----------------------------------------------------------
-
-def parse_bdist_wininst(name):
-    """Parse a bdist_win package name, and return it's package base name 
-    and version number if prossible.
-
-    Return a tuple of (base,pyversion) for possible .exe names"""
-
-    lower = name.lower()
-    base, py_ver = None, None
-
-    if lower.endswith('.exe'):
-        if lower.endswith('.win32.exe'):
-            base = name[:-10]
-        elif lower.startswith('.win32-py',-16):
-            py_ver = name[-7:-4]
-            base = name[:-16]
-
-    return base,py_ver
-
-def egg_info_for_url(url):
-    """
-    """
-    scheme, server, path, parameters, query, fragment = urlparse.urlparse(url)
-    base = urllib2.unquote(path.split('/')[-1])
-    if '#' in base:
-        base, fragment = base.split('#',1)
-    return base,fragment
-
-def distros_for_url(url, metadata=None):
-    """Yield egg or source distribution objects that might be found at a URL"""
-    base, fragment = egg_info_for_url(url)
-    for dist in distros_for_location(url, base, metadata): 
-        yield dist
-    if fragment:
-        match = EGG_FRAGMENT.match(fragment)
-        if match:
-            for dist in interpret_distro_name(url, match.group(1), metadata, 
-                precedence = CHECKOUT_DIST):
-                yield dist
-
-def distros_for_location(location, basename, metadata=None):
-    """Yield egg or source distribution objects based on basename"""
-    if basename.endswith('.egg.zip'):
-        basename = basename[:-4]    # strip the .zip
-    if basename.endswith('.egg') and '-' in basename:
-        # only one, unambiguous interpretation
-        return [Distribution.from_location(location, basename, metadata)]
-
-    if basename.endswith('.exe'):
-        win_base, py_ver = parse_bdist_wininst(basename)
-        if win_base is not None:
-            return interpret_distro_name(location, win_base, metadata, py_ver, 
-                BINARY_DIST, "win32")
-
-    # Try source distro extensions (.zip, .tgz, etc.)
-    for ext in EXTENSIONS:
-        if basename.endswith(ext):
-            basename = basename[:-len(ext)]
-            return interpret_distro_name(location, basename, metadata)
-    return []  # no extension matched
-
-def distros_for_filename(filename, metadata=None):
-    """Yield possible egg or source distribution objects based on a 
-    filename.
-    """
-    return distros_for_location(normalize_path(filename), 
-        os.path.basename(filename), metadata)
-
-def interpret_distro_name(location, basename, metadata,
-    py_version=None, precedence=SOURCE_DIST, platform=None):
-    """Generate alternative interpretations of a source distro name
-
-    Note: if `location` is a filesystem filename, you should call
-    ``pkg_resources.normalize_path()`` on it before passing it to this
-    routine!
-    """
-    # Generate alternative interpretations of a source distro name
-    # Because some packages are ambiguous as to name/versions split
-    # e.g. "adns-python-1.1.0", "egenix-mx-commercial", etc.
-    # So, we generate each possible interepretation (e.g. "adns, python-1.1.0"
-    # "adns-python, 1.1.0", and "adns-python-1.1.0, no version").  In practice,
-    # the spurious interpretations should be ignored, because in the event
-    # there's also an "adns" package, the spurious "python-1.1.0" version will
-    # compare lower than any numeric version number, and is therefore unlikely
-    # to match a request for it.  It's still a potential problem, though, and
-    # in the long run PyPI and the distutils should go for "safe" names and
-    # versions in distribution archive names (sdist and bdist).
-
-    parts = basename.split('-')
-    if not py_version:
-        for i,p in enumerate(parts[2:]):
-            if len(p)==5 and p.startswith('py2.'):
-                return # It's a bdist_dumb, not an sdist -- bail out
-
-    for p in range(1,len(parts)+1):
-        yield Distribution(
-            location, metadata, '-'.join(parts[:p]), '-'.join(parts[p:]),
-            py_version=py_version, precedence = precedence,
-            platform = platform)
-
-REL = re.compile("""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I)
-
-def find_external_links(url, page):
-    """Find rel="homepage" and rel="download" links in `page`, yielding URLs"""
-
-    for match in REL.finditer(page):
-        tag, rel = match.groups()
-        rels = map(str.strip, rel.lower().split(','))
-        if 'homepage' in rels or 'download' in rels:
-            for match in HREF.finditer(tag):
-                yield urlparse.urljoin(url, htmldecode(match.group(1)))
-
-    for tag in ("<th>Home Page", "<th>Download URL"):
-        pos = page.find(tag)
-        if pos!=-1:
-            match = HREF.search(page,pos)
-            if match:
-                yield urlparse.urljoin(url, htmldecode(match.group(1)))
-
-user_agent = "Python-urllib/%s distutils2/%s" % (
-    sys.version[:3], __distutils2_version__
-)
-
-class PackageIndex(Environment):
-    """A distribution index that scans web pages for download URLs"""
-
-    def __init__(self, index_url="http://pypi.python.org/simple", hosts=('*',),
-        *args, **kw):
-        self._init_logging()
-        Environment.__init__(self,*args,**kw)
-        self.index_url = index_url + "/"[:not index_url.endswith('/')]
-        self.scanned_urls = {}
-        self.fetched_urls = {}
-        self.package_pages = {}
-        self.allows = re.compile('|'.join(map(translate,hosts))).match
-        self.to_scan = []
-
-    def _init_logging(self):
-        import sys
-#        logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
-
-    def process_url(self, url, retrieve=False):
-        """Evaluate a URL as a possible download, and maybe retrieve it"""
-        if url in self.scanned_urls and not retrieve:
-            return
-        self.scanned_urls[url] = True
-        if not URL_SCHEME(url):
-            self.process_filename(url)
-            return
-        else:
-            dists = list(distros_for_url(url))
-            if dists:
-                if not self.url_ok(url):
-                    return
-                self.debug("Found link: %s", url)
-
-        if dists or not retrieve or url in self.fetched_urls:
-            map(self.add, dists)
-            return  # don't need the actual page
-
-        if not self.url_ok(url):
-            self.fetched_urls[url] = True
-            return
-
-        self.info("Reading %s", url)
-        f = self.open_url(url, "Download error: %s -- Some packages may not be found!")
-        if f is None: return
-        self.fetched_urls[url] = self.fetched_urls[f.url] = True
-
-        if 'html' not in f.headers.get('content-type', '').lower():
-            f.close()   # not html, we can't process it
-            return
-
-        base = f.url     # handle redirects
-        page = f.read()
-        if sys.version_info >= (3,):
-            charset = f.headers.get_param('charset') or 'latin-1'
-            page = page.decode(charset, "ignore")
-        f.close()
-        for match in HREF.finditer(page):
-            link = urlparse.urljoin(base, htmldecode(match.group(1)))
-            self.process_url(link)
-        if url.startswith(self.index_url) and getattr(f,'code',None)!=404:
-            page = self.process_index(url, page)
-
-    def process_filename(self, fn, nested=False):
-        # process filenames or directories
-        if not os.path.exists(fn):
-            self.warn("Not found: %s", fn)
-            return
-
-        if os.path.isdir(fn) and not nested:
-            path = os.path.realpath(fn)
-            for item in os.listdir(path):
-                self.process_filename(os.path.join(path,item), True)
-
-        dists = distros_for_filename(fn)
-        if dists:
-            self.debug("Found: %s", fn)
-            map(self.add, dists)
-
-    def url_ok(self, url, fatal=False):
-        s = URL_SCHEME(url)
-        if (s and s.group(1).lower()=='file') or self.allows(urlparse.urlparse(url)[1]):
-            return True
-        msg = "\nLink to % s ***BLOCKED*** by --allow-hosts\n"
-        if fatal:
-            raise DistutilsError(msg % url)
-        else:
-            self.warn(msg, url)
-        return False
-
-    def scan_egg_links(self, search_path):
-        for item in search_path:
-            if os.path.isdir(item):
-                for entry in os.listdir(item):
-                    if entry.endswith('.egg-link'):
-                        self.scan_egg_link(item, entry)
-
-    def scan_egg_link(self, path, entry):
-        lines = filter(None, map(str.strip, open(os.path.join(path, entry))))
-        if len(lines)==2:
-            for dist in find_distributions(os.path.join(path, lines[0])):
-                dist.location = os.path.join(path, *lines)
-                dist.precedence = SOURCE_DIST
-                self.add(dist)
-
-    def process_index(self,url,page):
-        """Process the contents of a PyPI page"""
-        def scan(link):
-            # Process a URL to see if it's for a package page
-            if link.startswith(self.index_url):
-                parts = map(
-                    urllib2.unquote, link[len(self.index_url):].split('/')
-                )
-                if len(parts)==2 and '#' not in parts[1]:
-                    # it's a package page, sanitize and index it
-                    pkg = safe_name(parts[0])
-                    ver = safe_version(parts[1])
-                    self.package_pages.setdefault(pkg.lower(),{})[link] = True
-                    return to_filename(pkg), to_filename(ver)
-            return None, None
-
-        # process an index page into the package-page index
-        for match in HREF.finditer(page):
-            try:
-                scan( urlparse.urljoin(url, htmldecode(match.group(1))) )
-            except ValueError:
-                pass
-
-        pkg, ver = scan(url)   # ensure this page is in the page index
-        if pkg:
-            # process individual package page
-            for new_url in find_external_links(url, page):
-                # Process the found URL
-                base, frag = egg_info_for_url(new_url)
-                if base.endswith('.py') and not frag:
-                    if ver:
-                        new_url+='#egg=%s-%s' % (pkg,ver)
-                    else:
-                        self.need_version_info(url)
-                self.scan_url(new_url)
-
-            return PYPI_MD5.sub(
-                lambda m: '<a href="%s#md5=%s">%s</a>' % m.group(1,3,2), page
-            )
-        else:
-            return ""   # no sense double-scanning non-package pages
-
-    def need_version_info(self, url):
-        self.scan_all(
-            "Page at %s links to .py file(s) without version info; an index "
-            "scan is required.", url
-        )
-
-    def scan_all(self, msg=None, *args):
-        if self.index_url not in self.fetched_urls:
-            if msg: self.warn(msg,*args)
-            self.info(
-                "Scanning index of all packages (this may take a while)"
-            )
-        self.scan_url(self.index_url)
-
-    def find_packages(self, requirement):
-        self.scan_url(self.index_url + requirement.unsafe_name+'/')
-
-        if not self.package_pages.get(requirement.key):
-            # Fall back to safe version of the name
-            self.scan_url(self.index_url + requirement.project_name+'/')
-
-        if not self.package_pages.get(requirement.key):
-            # We couldn't find the target package, so search the index page too
-            self.not_found_in_index(requirement)
-
-        for url in list(self.package_pages.get(requirement.key,())):
-            # scan each page that might be related to the desired package
-            self.scan_url(url)
-
-    def obtain(self, requirement, installer=None):
-        self.prescan(); self.find_packages(requirement)
-        for dist in self[requirement.key]:
-            if dist in requirement:
-                return dist
-            self.debug("%s does not match %s", requirement, dist)
-        return super(PackageIndex, self).obtain(requirement,installer)
-
-    def check_md5(self, cs, info, filename, tfp):
-        if re.match('md5=[0-9a-f]{32}$', info):
-            self.debug("Validating md5 checksum for %s", filename)
-            if cs.hexdigest()<>info[4:]:
-                tfp.close()
-                os.unlink(filename)
-                raise DistutilsError(
-                    "MD5 validation failed for "+os.path.basename(filename)+
-                    "; possible download problem?")
-
-    def add_find_links(self, urls):
-        """Add `urls` to the list that will be prescanned for searches"""
-        for url in urls:
-            if (
-                self.to_scan is None        # if we have already "gone online"
-                or not URL_SCHEME(url)      # or it's a local file/directory
-                or url.startswith('file:')
-                or list(distros_for_url(url))   # or a direct package link
-            ):
-                # then go ahead and process it now
-                self.scan_url(url)
-            else:
-                # otherwise, defer retrieval till later
-                self.to_scan.append(url)
-
-    def prescan(self):
-        """Scan urls scheduled for prescanning (e.g. --find-links)"""
-        if self.to_scan:
-            map(self.scan_url, self.to_scan)
-        self.to_scan = None     # from now on, go ahead and process immediately
-
-    def not_found_in_index(self, requirement):
-        if self[requirement.key]:   # we've seen at least one distro
-            meth, msg = self.info, "Couldn't retrieve index page for %r"
-        else:   # no distros seen for this name, might be misspelled
-            meth, msg = (self.warn,
-                "Couldn't find index page for %r (maybe misspelled?)")
-        meth(msg, requirement.unsafe_name)
-        self.scan_all()
-
-    def download(self, spec, tmpdir):
-        """Locate and/or download `spec` to `tmpdir`, returning a local path
-
-        `spec` may be a ``Requirement`` object, or a string containing a URL,
-        an existing local filename, or a project/version requirement spec
-        (i.e. the string form of a ``Requirement`` object).  If it is the URL
-        of a .py file with an unambiguous ``#egg=name-version`` tag (i.e., one
-        that escapes ``-`` as ``_`` throughout), a trivial ``setup.py`` is
-        automatically created alongside the downloaded file.
-
-        If `spec` is a ``Requirement`` object or a string containing a
-        project/version requirement spec, this method returns the location of
-        a matching distribution (possibly after downloading it to `tmpdir`).
-        If `spec` is a locally existing file or directory name, it is simply
-        returned unchanged.  If `spec` is a URL, it is downloaded to a subpath
-        of `tmpdir`, and the local filename is returned.  Various errors may be
-        raised if a problem occurs during downloading.
-        """
-        if not isinstance(spec,Requirement):
-            scheme = URL_SCHEME(spec)
-            if scheme:
-                # It's a url, download it to tmpdir
-                found = self._download_url(scheme.group(1), spec, tmpdir)
-                base, fragment = egg_info_for_url(spec)
-                if base.endswith('.py'):
-                    found = self.gen_setup(found,fragment,tmpdir)
-                return found
-            elif os.path.exists(spec):
-                # Existing file or directory, just return it
-                return spec
-            else:
-                try:
-                    spec = Requirement.parse(spec)
-                except ValueError:
-                    raise DistutilsError(
-                        "Not a URL, existing file, or requirement spec: %r" %
-                        (spec,)
-                    )
-        return getattr(self.fetch_distribution(spec, tmpdir),'location',None)
-
-    def fetch_distribution(self,
-        requirement, tmpdir, force_scan=False, source=False, develop_ok=False
-    ):
-        """Obtain a distribution suitable for fulfilling `requirement`
-
-        `requirement` must be a ``pkg_resources.Requirement`` instance.
-        If necessary, or if the `force_scan` flag is set, the requirement is
-        searched for in the (online) package index as well as the locally
-        installed packages.  If a distribution matching `requirement` is found,
-        the returned distribution's ``location`` is the value you would have
-        gotten from calling the ``download()`` method with the matching
-        distribution's URL or filename.  If no matching distribution is found,
-        ``None`` is returned.
-
-        If the `source` flag is set, only source distributions and source
-        checkout links will be considered.  Unless the `develop_ok` flag is
-        set, development and system eggs (i.e., those using the ``.egg-info``
-        format) will be ignored.
-        """
-        
-        # process a Requirement
-        self.info("Searching for %s", requirement)
-        skipped = {}
-
-        def find(req):
-            # Find a matching distribution; may be called more than once
-
-            for dist in self[req.key]:
-
-                if dist.precedence==DEVELOP_DIST and not develop_ok:
-                    if dist not in skipped:
-                        self.warn("Skipping development or system egg: %s",dist)
-                        skipped[dist] = 1
-                    continue
-
-                if dist in req and (dist.precedence<=SOURCE_DIST or not source):
-                    self.info("Best match: %s", dist)
-                    return dist.clone(
-                        location=self.download(dist.location, tmpdir)
-                    )
-
-        if force_scan:
-            self.prescan()
-            self.find_packages(requirement)
-
-        dist = find(requirement)
-        if dist is None and self.to_scan is not None:
-            self.prescan()
-            dist = find(requirement)
-
-        if dist is None and not force_scan:
-            self.find_packages(requirement)
-            dist = find(requirement)
-
-        if dist is None:
-            self.warn(
-                "No local packages or download links found for %s%s",
-                (source and "a source distribution of " or ""),
-                requirement,
-            )
-        return dist
-
-    def fetch(self, requirement, tmpdir, force_scan=False, source=False):
-        """Obtain a file suitable for fulfilling `requirement`
-
-        DEPRECATED; use the ``fetch_distribution()`` method now instead.  For
-        backward compatibility, this routine is identical but returns the
-        ``location`` of the downloaded distribution instead of a distribution
-        object.
-        """
-        dist = self.fetch_distribution(requirement,tmpdir,force_scan,source)
-        if dist is not None:
-            return dist.location
-        return None
-    
-    def gen_setup(self, filename, fragment, tmpdir):
-        match = EGG_FRAGMENT.match(fragment)
-        dists = match and [d for d in
-            interpret_distro_name(filename, match.group(1), None) if d.version
-        ] or []
-
-        if len(dists)==1:   # unambiguous ``#egg`` fragment
-            basename = os.path.basename(filename)
-
-            # Make sure the file has been downloaded to the temp dir.
-            if os.path.dirname(filename) != tmpdir:
-                dst = os.path.join(tmpdir, basename)
-                from setuptools.command.easy_install import samefile
-                if not samefile(filename, dst):
-                    shutil.copy2(filename, dst)
-                    filename=dst
-
-            file = open(os.path.join(tmpdir, 'setup.py'), 'w')
-            file.write(
-                "from setuptools import setup\n"
-                "setup(name=%r, version=%r, py_modules=[%r])\n"
-                % (
-                    dists[0].project_name, dists[0].version,
-                    os.path.splitext(basename)[0]
-                )
-            )
-            file.close()
-            return filename
-
-        elif match:
-            raise DistutilsError(
-                "Can't unambiguously interpret project/version identifier %r; "
-                "any dashes in the name or version should be escaped using "
-                "underscores. %r" % (fragment,dists)
-            )
-        else:
-            raise DistutilsError(
-                "Can't process plain .py without an '#egg=name-version'"
-                " suffix to enable automatic setup script generation."
-            )
-
-    dl_blocksize = 8192
-    def _download_to(self, url, filename):
-        self.info("Downloading %s", url)
-        # Download the file
-        fp, tfp, info = None, None, None
-        try:
-            if '#' in url:
-                url, info = url.split('#', 1)
-            fp = self.open_url(url)
-            if isinstance(fp, urllib2.HTTPError):
-                raise DistutilsError(
-                    "Can't download %s: %s %s" % (url, fp.code,fp.msg)
-                )
-            cs = md5()
-            headers = fp.info()
-            blocknum = 0
-            bs = self.dl_blocksize
-            size = -1
-            if "content-length" in headers:
-                size = int(headers["Content-Length"])
-                self.reporthook(url, filename, blocknum, bs, size)
-            tfp = open(filename,'wb')
-            while True:
-                block = fp.read(bs)
-                if block:
-                    cs.update(block)
-                    tfp.write(block)
-                    blocknum += 1
-                    self.reporthook(url, filename, blocknum, bs, size)
-                else:
-                    break
-            if info: self.check_md5(cs, info, filename, tfp)
-            return headers
-        finally:
-            if fp: fp.close()
-            if tfp: tfp.close()
-
-    def reporthook(self, url, filename, blocknum, blksize, size):
-        pass    # no-op
-
-    def open_url(self, url, warning=None):
-        if url.startswith('file:'):
-            return local_open(url)
-        try:
-            return open_with_auth(url)
-        except (ValueError, httplib.InvalidURL), v:
-            msg = ' '.join([str(arg) for arg in v.args])
-            if warning:
-                self.warn(warning, msg)
-            else:
-                raise DistutilsError('%s %s' % (url, msg))
-        except urllib2.HTTPError, v:
-            return v
-        except urllib2.URLError, v:
-            if warning:
-                self.warn(warning, v.reason)
-            else:
-                raise DistutilsError("Download error for %s: %s"
-                                     % (url, v.reason))
-        except httplib.BadStatusLine, v:
-            if warning:
-                self.warn(warning, v.line)
-            else:
-                raise DistutilsError('%s returned a bad status line. '
-                                     'The server might be down, %s' % \
-                                             (url, v.line))
-        except httplib.HTTPException, v:
-            if warning:
-                self.warn(warning, v)
-            else:
-                raise DistutilsError("Download error for %s: %s"
-                                     % (url, v))
-
-    def _download_url(self, scheme, url, tmpdir):
-        # Determine download filename
-        name = filter(None,urlparse.urlparse(url)[2].split('/'))
-        if name:
-            name = name[-1]
-            while '..' in name:
-                name = name.replace('..','.').replace('\\','_')
-        else:
-            name = "__downloaded__"    # default if URL has no path contents
-
-        if name.endswith('.egg.zip'):
-            name = name[:-4]    # strip the extra .zip before download
-
-        filename = os.path.join(tmpdir,name)
-
-        # Download the file
-        if scheme=='svn' or scheme.startswith('svn+'):
-            return self._download_svn(url, filename)
-        elif scheme=='file':
-            return urllib2.url2pathname(urlparse.urlparse(url)[2])
-        else:
-            self.url_ok(url, True)   # raises error if not allowed
-            return self._attempt_download(url, filename)
-
-    def scan_url(self, url):
-        self.process_url(url, True)
-
-    def _attempt_download(self, url, filename):
-        headers = self._download_to(url, filename)
-        if 'html' in headers.get('content-type','').lower():
-            return self._download_html(url, headers, filename)
-        else:
-            return filename
-
-    def _download_html(self, url, headers, filename):
-        file = open(filename)
-        for line in file:
-            if line.strip():
-                # Check for a subversion index page
-                if re.search(r'<title>([^- ]+ - )?Revision \d+:', line):
-                    # it's a subversion index page:
-                    file.close()
-                    os.unlink(filename)
-                    return self._download_svn(url, filename)
-                break   # not an index page
-        file.close()
-        os.unlink(filename)
-        raise DistutilsError("Unexpected HTML page found at "+url)
-
-    def _download_svn(self, url, filename):
-        url = url.split('#',1)[0]   # remove any fragment for svn's sake
-        self.info("Doing subversion checkout from %s to %s", url, filename)
-        os.system("svn checkout -q %s %s" % (url, filename))
-        return filename
-
-    def debug(self, msg, *args):
-        logging.debug(msg, *args)
-
-    def info(self, msg, *args):
-        logging.info(msg, *args)
-
-    def warn(self, msg, *args):
-        logging.warn(msg, *args)
+    'href="[^?]+\?:action=show_md5&amp;digest=([0-9a-f]{32})">md5</a>\\)')
+URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):', re.I).match
 
 # This pattern matches a character entity reference (a decimal numeric
 # references, a hexadecimal numeric reference, or a named reference).
-entity_sub = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub
+ENTITY_SUB = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub
+REL = re.compile("""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I)
 
-def uchr(c):
-    if not isinstance(c, int):
-        return c
-    if c>255: 
-        return unichr(c)
-    return chr(c)
 
-def decode_entity(match):
-    what = match.group(1)
-    if what.startswith('#x'):
-        what = int(what[2:], 16)
-    elif what.startswith('#'):
-        what = int(what[1:])
-    else:
-        from htmlentitydefs import name2codepoint
-        what = name2codepoint.get(what, match.group(0))
-    return uchr(what)
-
-def htmldecode(text):
-    """Decode HTML entities in the given text."""
-    return entity_sub(decode_entity, text)
-
-def socket_timeout(timeout=15):
+def socket_timeout(timeout=SOCKET_TIMEOUT):
+    """Decorator to add a socket timeout when requesting pages on PyPI.
+    """
     def _socket_timeout(func):
-        def _socket_timeout(*args, **kwargs):
+        def _socket_timeout(self, *args, **kwargs):
             old_timeout = socket.getdefaulttimeout()
+            if hasattr(self, "_timeout"):
+                timeout = self._timeout
             socket.setdefaulttimeout(timeout)
             try:
-                return func(*args, **kwargs)
+                return func(self, *args, **kwargs)
             finally:
                 socket.setdefaulttimeout(old_timeout)
         return _socket_timeout
     return _socket_timeout
 
-def open_with_auth(url):
-    """Open a urllib2 request, handling HTTP authentication"""
 
-    scheme, netloc, path, params, query, frag = urlparse.urlparse(url)
+class SimpleIndex(object):
+    """Provides useful tools to request the Python Package Index simple API
 
-    if scheme in ('http', 'https'):
-        auth, host = urllib2.splituser(netloc)
-    else:
-        auth = None
+    :param index_url: the url of the simple index to search on.
+    :param follow_externals: tell if following external links is needed or
+                             not. Default is False.
+    :param hosts: a list of hosts allowed to be processed while using
+                  follow_externals=True. Default behavior is to follow all
+                  hosts.
+    :param follow_externals: tell if following external links is needed or
+                             not. Default is False.
+    :param prefer_source: if there is binary and source distributions, the
+                          source prevails.
+    :param prefer_final: if the version is not mentioned, and the last
+                         version is not a "final" one (alpha, beta, etc.),
+                         pick up the last final version.
+    :param mirrors_url: the url to look on for DNS records giving mirror
+                        adresses.
+    :param mirrors: a list of mirrors to check out if problems
+                         occurs while working with the one given in "url"
+    :param timeout: time in seconds to consider a url has timeouted.
+    """
 
-    if auth:
-        auth = "Basic " + urllib2.unquote(auth).encode('base64').strip()
-        new_url = urlparse.urlunparse((scheme,host,path,params,query,frag))
-        request = urllib2.Request(new_url)
-        request.add_header("Authorization", auth)
-    else:
-        request = urllib2.Request(url)
+    def __init__(self, index_url=PYPI_DEFAULT_INDEX_URL, hosts=DEFAULT_HOSTS,
+                 follow_externals=False, prefer_source=True,
+                 prefer_final=False, mirrors_url=PYPI_DEFAULT_MIRROR_URL,
+                 mirrors=None, timeout=SOCKET_TIMEOUT):
+        self.follow_externals = follow_externals
 
-    request.add_header('User-Agent', user_agent)
-    fp = urllib2.urlopen(request)
+        if not index_url.endswith("/"):
+            index_url += "/"
+        self._index_urls = [index_url]
+        # if no mirrors are defined, use the method described in PEP 381.
+        if mirrors is None:
+            try:
+                mirrors = socket.gethostbyname_ex(mirrors_url)[-1]
+            except socket.gaierror:
+                mirrors = []
+        self._index_urls.extend(mirrors)
+        self._current_index_url = 0
+        self._timeout = timeout
+        self._prefer_source = prefer_source
+        self._prefer_final = prefer_final
 
-    if auth:
-        # Put authentication info back into request URL if same host,
-        # so that links found on the page will work
-        s2, h2, path2, param2, query2, frag2 = urlparse.urlparse(fp.url)
-        if s2==scheme and h2==host:
-            fp.url = urlparse.urlunparse((s2,netloc,path2,param2,query2,frag2))
+        # create a regexp to match all given hosts
+        self._allowed_hosts = re.compile('|'.join(map(translate, hosts))).match
 
-    return fp
+        # we keep an index of pages we have processed, in order to avoid
+        # scanning them multple time (eg. if there is multiple pages pointing
+        # on one)
+        self._processed_urls = []
+        self._distributions = {}
 
+    def find(self, requirements, prefer_source=None, prefer_final=None):
+        """Browse the PyPI to find distributions that fullfil the given
+        requirements.
 
-# adding a timeout to avoid freezing package_index
-open_with_auth = socket_timeout(_SOCKET_TIMEOUT)(open_with_auth)
+        :param requirements: A project name and it's distribution, using
+                             version specifiers, as described in PEP345.
+        :type requirements:  You can pass either a version.VersionPredicate
+                             or a string.
+        :param prefer_source: if there is binary and source distributions, the
+                              source prevails.
+        :param prefer_final: if the version is not mentioned, and the last
+                             version is not a "final" one (alpha, beta, etc.),
+                             pick up the last final version.
+        """
+        requirements = self._get_version_predicate(requirements)
+        if prefer_source is None:
+            prefer_source = self._prefer_source
+        if prefer_final is None:
+            prefer_final = self._prefer_final
 
-def fix_sf_url(url):
-    return url      # backward compatibility
+        # process the index for this project
+        self._process_pypi_page(requirements.name)
 
-def local_open(url):
-    """Read a local path, with special support for directories"""
-    scheme, server, path, param, query, frag = urlparse.urlparse(url)
-    filename = urllib2.url2pathname(path)
-    if os.path.isfile(filename):
-        return urllib2.urlopen(url)
-    elif path.endswith('/') and os.path.isdir(filename):
-        files = []
-        for f in os.listdir(filename):
-            if f=='index.html':
-                fp = open(os.path.join(filename,f),'rb')
-                body = fp.read()
-                fp.close()
-                break
-            elif os.path.isdir(os.path.join(filename,f)):
-                f+='/'
-            files.append("<a href=%r>%s</a>" % (f,f))
+        # filter with requirements and return the results
+        if requirements.name in self._distributions:
+            dists = self._distributions[requirements.name].filter(requirements)
+            dists.sort_distributions(prefer_source=prefer_source,
+                                     prefer_final=prefer_final)
         else:
-            body = ("<html><head><title>%s</title>" % url) + \
-                "</head><body>%s</body></html>" % '\n'.join(files)
-        status, message = 200, "OK"
-    else:
-        status, message, body = 404, "Path not found", "Not found"
+            dists = []
 
-    return urllib2.HTTPError(url, status, message,
-            {'content-type':'text/html'}, cStringIO.StringIO(body))
+        return dists
+
+    def get(self, requirements, *args, **kwargs):
+        """Browse the PyPI index to find distributions that fullfil the
+        given requirements, and return the most recent one.
+
+        You can specify prefer_final and prefer_source arguments here.
+        If not, the default one will be used.
+        """
+        predicate = self._get_version_predicate(requirements)
+        dists = self.find(predicate, *args, **kwargs)
+
+        if len(dists) == 0:
+            raise DistributionNotFound(requirements)
+
+        return dists.get_last(predicate)
+
+    def download(self, requirements, temp_path=None, *args, **kwargs):
+        """Download the distribution, using the requirements.
+
+        If more than one distribution match the requirements, use the last
+        version.
+        Download the distribution, and put it in the temp_path. If no temp_path
+        is given, creates and return one.
+
+        Returns the complete absolute path to the downloaded archive.
+
+        :param requirements: The same as the find attribute of `find`.
+
+        You can specify prefer_final and prefer_source arguments here.
+        If not, the default one will be used.
+        """
+        return self.get(requirements, *args, **kwargs)\
+                   .download(path=temp_path)
+
+    def _get_version_predicate(self, requirements):
+        """Return a VersionPredicate object, from a string or an already
+        existing object.
+        """
+        if isinstance(requirements, str):
+            requirements = VersionPredicate(requirements)
+        return requirements
+
+    @property
+    def index_url(self):
+        return self._index_urls[self._current_index_url]
+
+    def _switch_to_next_mirror(self):
+        """Switch to the next mirror (eg. point self.index_url to the next
+        url.
+        """
+        # Internally, iter over the _index_url iterable, if we have read all
+        # of the available indexes, raise an exception.
+        if self._current_index_url < len(self._index_urls):
+            self._current_index_url = self._current_index_url + 1
+        else:
+            raise UnableToDownload("All mirrors fails")
+
+    def _is_browsable(self, url):
+        """Tell if the given URL can be browsed or not.
+
+        It uses the follow_externals and the hosts list to tell if the given
+        url is browsable or not.
+        """
+        # if _index_url is contained in the given URL, we are browsing the
+        # index, and it's always "browsable".
+        # local files are always considered browable resources
+        if self.index_url in url or urlparse.urlparse(url)[0] == "file":
+            return True
+        elif self.follow_externals:
+            if self._allowed_hosts(urlparse.urlparse(url)[1]):  # 1 is netloc
+                return True
+            else:
+                return False
+        return False
+
+    def _is_distribution(self, link):
+        """Tell if the given URL matches to a distribution name or not.
+        """
+        #XXX find a better way to check that links are distributions
+        # Using a regexp ?
+        for ext in EXTENSIONS:
+            if ext in link:
+                return True
+        return False
+
+    def _register_dist(self, dist):
+        """Register a distribution as a part of fetched distributions for
+        SimpleIndex.
+
+        Return the PyPIDistributions object for the specified project name
+        """
+        # Internally, check if a entry exists with the project name, if not,
+        # create a new one, and if exists, add the dist to the pool.
+        if not dist.name in self._distributions:
+            self._distributions[dist.name] = PyPIDistributions()
+        self._distributions[dist.name].append(dist)
+        return self._distributions[dist.name]
+
+    def _process_url(self, url, project_name=None, follow_links=True):
+        """Process an url and search for distributions packages.
+
+        For each URL found, if it's a download, creates a PyPIdistribution
+        object. If it's a homepage and we can follow links, process it too.
+
+        :param url: the url to process
+        :param project_name: the project name we are searching for.
+        :param follow_links: Do not want to follow links more than from one
+                             level. This parameter tells if we want to follow
+                             the links we find (eg. run recursively this
+                             method on it)
+        """
+        f = self._open_url(url)
+        base_url = f.url
+        if url not in self._processed_urls:
+            self._processed_urls.append(url)
+            link_matcher = self._get_link_matcher(url)
+            for link, is_download in link_matcher(f.read(), base_url):
+                if link not in self._processed_urls:
+                    if self._is_distribution(link) or is_download:
+                        self._processed_urls.append(link)
+                        # it's a distribution, so create a dist object
+                        dist = PyPIDistribution.from_url(link, project_name,
+                                    is_external=not self.index_url in url)
+                        self._register_dist(dist)
+                    else:
+                        if self._is_browsable(link) and follow_links:
+                            self._process_url(link, project_name,
+                                follow_links=False)
+
+    def _get_link_matcher(self, url):
+        """Returns the right link matcher function of the given url
+        """
+        if self.index_url in url:
+            return self._simple_link_matcher
+        else:
+            return self._default_link_matcher
+
+    def _simple_link_matcher(self, content, base_url):
+        """Yield all links with a rel="download" or rel="homepage".
+
+        This matches the simple index requirements for matching links.
+        If follow_externals is set to False, dont yeld the external
+        urls.
+        """
+        for match in REL.finditer(content):
+            tag, rel = match.groups()
+            rels = map(str.strip, rel.lower().split(','))
+            if 'homepage' in rels or 'download' in rels:
+                for match in HREF.finditer(tag):
+                    url = urlparse.urljoin(base_url,
+                                           self._htmldecode(match.group(1)))
+                    if 'download' in rels or self._is_browsable(url):
+                        # yield a list of (url, is_download)
+                        yield (urlparse.urljoin(base_url, url),
+                               'download' in rels)
+
+    def _default_link_matcher(self, content, base_url):
+        """Yield all links found on the page.
+        """
+        for match in HREF.finditer(content):
+            url = urlparse.urljoin(base_url, self._htmldecode(match.group(1)))
+            if self._is_browsable(url):
+                yield (url, False)
+
+    def _process_pypi_page(self, name):
+        """Find and process a PyPI page for the given project name.
+
+        :param name: the name of the project to find the page
+        """
+        try:
+            # Browse and index the content of the given PyPI page.
+            url = self.index_url + name + "/"
+            self._process_url(url, name)
+        except DownloadError:
+            # if an error occurs, try with the next index_url
+            # (provided by the mirrors)
+            self._switch_to_next_mirror()
+            self._distributions.clear()
+            self._process_pypi_page(name)
+
+    @socket_timeout()
+    def _open_url(self, url):
+        """Open a urllib2 request, handling HTTP authentication, and local
+        files support.
+
+        """
+        try:
+            scheme, netloc, path, params, query, frag = urlparse.urlparse(url)
+
+            if scheme in ('http', 'https'):
+                auth, host = urllib2.splituser(netloc)
+            else:
+                auth = None
+
+            # add index.html automatically for filesystem paths
+            if scheme == 'file':
+                if url.endswith('/'):
+                    url += "index.html"
+
+            if auth:
+                auth = "Basic " + \
+                    urllib2.unquote(auth).encode('base64').strip()
+                new_url = urlparse.urlunparse((
+                    scheme, host, path, params, query, frag))
+                request = urllib2.Request(new_url)
+                request.add_header("Authorization", auth)
+            else:
+                request = urllib2.Request(url)
+            request.add_header('User-Agent', USER_AGENT)
+            fp = urllib2.urlopen(request)
+
+            if auth:
+                # Put authentication info back into request URL if same host,
+                # so that links found on the page will work
+                s2, h2, path2, param2, query2, frag2 = \
+                    urlparse.urlparse(fp.url)
+                if s2 == scheme and h2 == host:
+                    fp.url = urlparse.urlunparse(
+                        (s2, netloc, path2, param2, query2, frag2))
+
+            return fp
+        except (ValueError, httplib.InvalidURL), v:
+            msg = ' '.join([str(arg) for arg in v.args])
+            raise PyPIError('%s %s' % (url, msg))
+        except urllib2.HTTPError, v:
+            return v
+        except urllib2.URLError, v:
+            raise DownloadError("Download error for %s: %s" % (url, v.reason))
+        except httplib.BadStatusLine, v:
+            raise DownloadError('%s returned a bad status line. '
+                'The server might be down, %s' % (url, v.line))
+        except httplib.HTTPException, v:
+            raise DownloadError("Download error for %s: %s" % (url, v))
+
+    def _decode_entity(self, match):
+        what = match.group(1)
+        if what.startswith('#x'):
+            what = int(what[2:], 16)
+        elif what.startswith('#'):
+            what = int(what[1:])
+        else:
+            from htmlentitydefs import name2codepoint
+            what = name2codepoint.get(what, match.group(0))
+        return unichr(what)
+
+    def _htmldecode(self, text):
+        """Decode HTML entities in the given text."""
+        return ENTITY_SUB(self._decode_entity, text)
diff --git a/src/distutils2/spawn.py b/src/distutils2/spawn.py
deleted file mode 100644
--- a/src/distutils2/spawn.py
+++ /dev/null
@@ -1,173 +0,0 @@
-"""distutils.spawn
-
-Provides the 'spawn()' function, a front-end to various platform-
-specific functions for launching another program in a sub-process.
-Also provides the 'find_executable()' to search the path for a given
-executable name.
-"""
-
-__revision__ = "$Id: spawn.py 73147 2009-06-02 15:58:43Z tarek.ziade $"
-
-import sys
-import os
-
-from distutils2.errors import DistutilsPlatformError, DistutilsExecError
-from distutils2 import log
-
-def spawn(cmd, search_path=1, verbose=0, dry_run=0):
-    """Run another program, specified as a command list 'cmd', in a new process.
-
-    'cmd' is just the argument list for the new process, ie.
-    cmd[0] is the program to run and cmd[1:] are the rest of its arguments.
-    There is no way to run a program with a name different from that of its
-    executable.
-
-    If 'search_path' is true (the default), the system's executable
-    search path will be used to find the program; otherwise, cmd[0]
-    must be the exact path to the executable.  If 'dry_run' is true,
-    the command will not actually be run.
-
-    Raise DistutilsExecError if running the program fails in any way; just
-    return on success.
-    """
-    if os.name == 'posix':
-        _spawn_posix(cmd, search_path, dry_run=dry_run)
-    elif os.name == 'nt':
-        _spawn_nt(cmd, search_path, dry_run=dry_run)
-    elif os.name == 'os2':
-        _spawn_os2(cmd, search_path, dry_run=dry_run)
-    else:
-        raise DistutilsPlatformError, \
-              "don't know how to spawn programs on platform '%s'" % os.name
-
-def _nt_quote_args(args):
-    """Quote command-line arguments for DOS/Windows conventions.
-
-    Just wraps every argument which contains blanks in double quotes, and
-    returns a new argument list.
-    """
-    # XXX this doesn't seem very robust to me -- but if the Windows guys
-    # say it'll work, I guess I'll have to accept it.  (What if an arg
-    # contains quotes?  What other magic characters, other than spaces,
-    # have to be escaped?  Is there an escaping mechanism other than
-    # quoting?)
-    for i, arg in enumerate(args):
-        if ' ' in arg:
-            args[i] = '"%s"' % arg
-    return args
-
-def _spawn_nt(cmd, search_path=1, verbose=0, dry_run=0):
-    executable = cmd[0]
-    cmd = _nt_quote_args(cmd)
-    if search_path:
-        # either we find one or it stays the same
-        executable = find_executable(executable) or executable
-    log.info(' '.join([executable] + cmd[1:]))
-    if not dry_run:
-        # spawn for NT requires a full path to the .exe
-        try:
-            rc = os.spawnv(os.P_WAIT, executable, cmd)
-        except OSError, exc:
-            # this seems to happen when the command isn't found
-            raise DistutilsExecError, \
-                  "command '%s' failed: %s" % (cmd[0], exc[-1])
-        if rc != 0:
-            # and this reflects the command running but failing
-            raise DistutilsExecError, \
-                  "command '%s' failed with exit status %d" % (cmd[0], rc)
-
-def _spawn_os2(cmd, search_path=1, verbose=0, dry_run=0):
-    executable = cmd[0]
-    if search_path:
-        # either we find one or it stays the same
-        executable = find_executable(executable) or executable
-    log.info(' '.join([executable] + cmd[1:]))
-    if not dry_run:
-        # spawnv for OS/2 EMX requires a full path to the .exe
-        try:
-            rc = os.spawnv(os.P_WAIT, executable, cmd)
-        except OSError, exc:
-            # this seems to happen when the command isn't found
-            raise DistutilsExecError, \
-                  "command '%s' failed: %s" % (cmd[0], exc[-1])
-        if rc != 0:
-            # and this reflects the command running but failing
-            log.debug("command '%s' failed with exit status %d" % (cmd[0], rc))
-            raise DistutilsExecError, \
-                  "command '%s' failed with exit status %d" % (cmd[0], rc)
-
-
-def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0):
-    log.info(' '.join(cmd))
-    if dry_run:
-        return
-    exec_fn = search_path and os.execvp or os.execv
-    pid = os.fork()
-
-    if pid == 0:  # in the child
-        try:
-            exec_fn(cmd[0], cmd)
-        except OSError, e:
-            sys.stderr.write("unable to execute %s: %s\n" %
-                             (cmd[0], e.strerror))
-            os._exit(1)
-
-        sys.stderr.write("unable to execute %s for unknown reasons" % cmd[0])
-        os._exit(1)
-    else:   # in the parent
-        # Loop until the child either exits or is terminated by a signal
-        # (ie. keep waiting if it's merely stopped)
-        while 1:
-            try:
-                pid, status = os.waitpid(pid, 0)
-            except OSError, exc:
-                import errno
-                if exc.errno == errno.EINTR:
-                    continue
-                raise DistutilsExecError, \
-                      "command '%s' failed: %s" % (cmd[0], exc[-1])
-            if os.WIFSIGNALED(status):
-                raise DistutilsExecError, \
-                      "command '%s' terminated by signal %d" % \
-                      (cmd[0], os.WTERMSIG(status))
-
-            elif os.WIFEXITED(status):
-                exit_status = os.WEXITSTATUS(status)
-                if exit_status == 0:
-                    return   # hey, it succeeded!
-                else:
-                    raise DistutilsExecError, \
-                          "command '%s' failed with exit status %d" % \
-                          (cmd[0], exit_status)
-
-            elif os.WIFSTOPPED(status):
-                continue
-
-            else:
-                raise DistutilsExecError, \
-                      "unknown error executing '%s': termination status %d" % \
-                      (cmd[0], status)
-
-def find_executable(executable, path=None):
-    """Tries to find 'executable' in the directories listed in 'path'.
-
-    A string listing directories separated by 'os.pathsep'; defaults to
-    os.environ['PATH'].  Returns the complete filename or None if not found.
-    """
-    if path is None:
-        path = os.environ['PATH']
-    paths = path.split(os.pathsep)
-    base, ext = os.path.splitext(executable)
-
-    if (sys.platform == 'win32' or os.name == 'os2') and (ext != '.exe'):
-        executable = executable + '.exe'
-
-    if not os.path.isfile(executable):
-        for p in paths:
-            f = os.path.join(p, executable)
-            if os.path.isfile(f):
-                # the file exists, we have a shot at spawn working
-                return f
-        return None
-    else:
-        return executable
diff --git a/src/distutils2/tests/__init__.py b/src/distutils2/tests/__init__.py
--- a/src/distutils2/tests/__init__.py
+++ b/src/distutils2/tests/__init__.py
@@ -45,7 +45,7 @@
     """Test failed."""
 
 
-class BasicTestRunner:
+class BasicTestRunner(object):
     def run(self, test):
         result = unittest.TestResult()
         test(result)
diff --git a/src/distutils2/tests/conversions/05_after.py b/src/distutils2/tests/conversions/05_after.py
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/conversions/05_after.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2003-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://trac.edgewall.org/wiki/TracLicense.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://trac.edgewall.org/log/.
+
+from distutils2.core import setup, find_packages
+
+extra = {}
+
+try:
+    import babel
+    
+    extractors = [
+        ('**.py',                'python', None),
+        ('**/templates/**.html', 'genshi', None),
+        ('**/templates/**.txt',  'genshi',
+         {'template_class': 'genshi.template:NewTextTemplate'}),
+    ]
+    extra['message_extractors'] = {
+        'trac': extractors,
+        'tracopt': extractors,
+    }
+
+    from trac.util.dist import get_l10n_js_cmdclass
+    extra['cmdclass'] = get_l10n_js_cmdclass()
+
+except ImportError, e:
+    pass
+
+setup(
+    name = 'Trac',
+    version = '0.12.1',
+    summary = 'Integrated SCM, wiki, issue tracker and project environment',
+    description = """
+Trac is a minimalistic web-based software project management and bug/issue
+tracking system. It provides an interface to the Subversion revision control
+systems, an integrated wiki, flexible issue tracking and convenient report
+facilities.
+""",
+    author = 'Edgewall Software',
+    author_email = 'info at edgewall.com',
+    license = 'BSD',
+    home_page = 'http://trac.edgewall.org/',
+    download_url = 'http://trac.edgewall.org/wiki/TracDownload',
+    classifiers = [
+        'Environment :: Web Environment',
+        'Framework :: Trac',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: BSD License',
+        'Operating System :: OS Independent',
+        'Programming Language :: Python',
+        'Topic :: Software Development :: Bug Tracking',
+        'Topic :: Software Development :: Version Control',
+    ],
+
+    packages = find_packages(exclude=['*.tests']),
+    package_data = {
+        '': ['templates/*'],
+        'trac': ['htdocs/*.*', 'htdocs/README', 'htdocs/js/*.*',
+                 'htdocs/js/messages/*.*', 'htdocs/css/*.*',
+                 'htdocs/guide/*', 'locale/*/LC_MESSAGES/messages.mo'],
+        'trac.wiki': ['default-pages/*'],
+        'trac.ticket': ['workflows/*.ini'],
+    },
+
+    test_suite = 'trac.test.suite',
+    zip_safe = True,
+
+    requires_dist = [
+        'setuptools>=0.6b1',
+        'Genshi>=0.6',
+    ],
+    extras_require = {
+        'Babel': ['Babel>=0.9.5'],
+        'Pygments': ['Pygments>=0.6'],
+        'reST': ['docutils>=0.3'],
+        'SilverCity': ['SilverCity>=0.9.4'],
+        'Textile': ['textile>=2.0'],
+    },
+
+    entry_points = """
+        [console_scripts]
+        trac-admin = trac.admin.console:run
+        tracd = trac.web.standalone:main
+
+        [trac.plugins]
+        trac.about = trac.about
+        trac.admin.console = trac.admin.console
+        trac.admin.web_ui = trac.admin.web_ui
+        trac.attachment = trac.attachment
+        trac.db.mysql = trac.db.mysql_backend
+        trac.db.postgres = trac.db.postgres_backend
+        trac.db.sqlite = trac.db.sqlite_backend
+        trac.mimeview.patch = trac.mimeview.patch
+        trac.mimeview.pygments = trac.mimeview.pygments[Pygments]
+        trac.mimeview.rst = trac.mimeview.rst[reST]
+        trac.mimeview.silvercity = trac.mimeview.silvercity[SilverCity]
+        trac.mimeview.txtl = trac.mimeview.txtl[Textile]
+        trac.prefs = trac.prefs.web_ui
+        trac.search = trac.search.web_ui
+        trac.ticket.admin = trac.ticket.admin
+        trac.ticket.query = trac.ticket.query
+        trac.ticket.report = trac.ticket.report
+        trac.ticket.roadmap = trac.ticket.roadmap
+        trac.ticket.web_ui = trac.ticket.web_ui
+        trac.timeline = trac.timeline.web_ui
+        trac.versioncontrol.admin = trac.versioncontrol.admin
+        trac.versioncontrol.svn_authz = trac.versioncontrol.svn_authz
+        trac.versioncontrol.svn_fs = trac.versioncontrol.svn_fs
+        trac.versioncontrol.svn_prop = trac.versioncontrol.svn_prop
+        trac.versioncontrol.web_ui = trac.versioncontrol.web_ui
+        trac.web.auth = trac.web.auth
+        trac.web.session = trac.web.session
+        trac.wiki.admin = trac.wiki.admin
+        trac.wiki.interwiki = trac.wiki.interwiki
+        trac.wiki.macros = trac.wiki.macros
+        trac.wiki.web_ui = trac.wiki.web_ui
+        trac.wiki.web_api = trac.wiki.web_api
+        tracopt.mimeview.enscript = tracopt.mimeview.enscript
+        tracopt.mimeview.php = tracopt.mimeview.php
+        tracopt.perm.authz_policy = tracopt.perm.authz_policy
+        tracopt.perm.config_perm_provider = tracopt.perm.config_perm_provider
+        tracopt.ticket.commit_updater = tracopt.ticket.commit_updater
+        tracopt.ticket.deleter = tracopt.ticket.deleter
+    """,
+
+    **extra
+)
diff --git a/src/distutils2/tests/conversions/05_before.py b/src/distutils2/tests/conversions/05_before.py
new file mode 100755
--- /dev/null
+++ b/src/distutils2/tests/conversions/05_before.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2003-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://trac.edgewall.org/wiki/TracLicense.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://trac.edgewall.org/log/.
+
+from setuptools import setup, find_packages
+
+extra = {}
+
+try:
+    import babel
+    
+    extractors = [
+        ('**.py',                'python', None),
+        ('**/templates/**.html', 'genshi', None),
+        ('**/templates/**.txt',  'genshi',
+         {'template_class': 'genshi.template:NewTextTemplate'}),
+    ]
+    extra['message_extractors'] = {
+        'trac': extractors,
+        'tracopt': extractors,
+    }
+
+    from trac.util.dist import get_l10n_js_cmdclass
+    extra['cmdclass'] = get_l10n_js_cmdclass()
+
+except ImportError, e:
+    pass
+
+setup(
+    name = 'Trac',
+    version = '0.12.1',
+    description = 'Integrated SCM, wiki, issue tracker and project environment',
+    long_description = """
+Trac is a minimalistic web-based software project management and bug/issue
+tracking system. It provides an interface to the Subversion revision control
+systems, an integrated wiki, flexible issue tracking and convenient report
+facilities.
+""",
+    author = 'Edgewall Software',
+    author_email = 'info at edgewall.com',
+    license = 'BSD',
+    url = 'http://trac.edgewall.org/',
+    download_url = 'http://trac.edgewall.org/wiki/TracDownload',
+    classifiers = [
+        'Environment :: Web Environment',
+        'Framework :: Trac',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: BSD License',
+        'Operating System :: OS Independent',
+        'Programming Language :: Python',
+        'Topic :: Software Development :: Bug Tracking',
+        'Topic :: Software Development :: Version Control',
+    ],
+
+    packages = find_packages(exclude=['*.tests']),
+    package_data = {
+        '': ['templates/*'],
+        'trac': ['htdocs/*.*', 'htdocs/README', 'htdocs/js/*.*',
+                 'htdocs/js/messages/*.*', 'htdocs/css/*.*',
+                 'htdocs/guide/*', 'locale/*/LC_MESSAGES/messages.mo'],
+        'trac.wiki': ['default-pages/*'],
+        'trac.ticket': ['workflows/*.ini'],
+    },
+
+    test_suite = 'trac.test.suite',
+    zip_safe = True,
+
+    install_requires = [
+        'setuptools>=0.6b1',
+        'Genshi>=0.6',
+    ],
+    extras_require = {
+        'Babel': ['Babel>=0.9.5'],
+        'Pygments': ['Pygments>=0.6'],
+        'reST': ['docutils>=0.3'],
+        'SilverCity': ['SilverCity>=0.9.4'],
+        'Textile': ['textile>=2.0'],
+    },
+
+    entry_points = """
+        [console_scripts]
+        trac-admin = trac.admin.console:run
+        tracd = trac.web.standalone:main
+
+        [trac.plugins]
+        trac.about = trac.about
+        trac.admin.console = trac.admin.console
+        trac.admin.web_ui = trac.admin.web_ui
+        trac.attachment = trac.attachment
+        trac.db.mysql = trac.db.mysql_backend
+        trac.db.postgres = trac.db.postgres_backend
+        trac.db.sqlite = trac.db.sqlite_backend
+        trac.mimeview.patch = trac.mimeview.patch
+        trac.mimeview.pygments = trac.mimeview.pygments[Pygments]
+        trac.mimeview.rst = trac.mimeview.rst[reST]
+        trac.mimeview.silvercity = trac.mimeview.silvercity[SilverCity]
+        trac.mimeview.txtl = trac.mimeview.txtl[Textile]
+        trac.prefs = trac.prefs.web_ui
+        trac.search = trac.search.web_ui
+        trac.ticket.admin = trac.ticket.admin
+        trac.ticket.query = trac.ticket.query
+        trac.ticket.report = trac.ticket.report
+        trac.ticket.roadmap = trac.ticket.roadmap
+        trac.ticket.web_ui = trac.ticket.web_ui
+        trac.timeline = trac.timeline.web_ui
+        trac.versioncontrol.admin = trac.versioncontrol.admin
+        trac.versioncontrol.svn_authz = trac.versioncontrol.svn_authz
+        trac.versioncontrol.svn_fs = trac.versioncontrol.svn_fs
+        trac.versioncontrol.svn_prop = trac.versioncontrol.svn_prop
+        trac.versioncontrol.web_ui = trac.versioncontrol.web_ui
+        trac.web.auth = trac.web.auth
+        trac.web.session = trac.web.session
+        trac.wiki.admin = trac.wiki.admin
+        trac.wiki.interwiki = trac.wiki.interwiki
+        trac.wiki.macros = trac.wiki.macros
+        trac.wiki.web_ui = trac.wiki.web_ui
+        trac.wiki.web_api = trac.wiki.web_api
+        tracopt.mimeview.enscript = tracopt.mimeview.enscript
+        tracopt.mimeview.php = tracopt.mimeview.php
+        tracopt.perm.authz_policy = tracopt.perm.authz_policy
+        tracopt.perm.config_perm_provider = tracopt.perm.config_perm_provider
+        tracopt.ticket.commit_updater = tracopt.ticket.commit_updater
+        tracopt.ticket.deleter = tracopt.ticket.deleter
+    """,
+
+    **extra
+)
diff --git a/src/distutils2/tests/pypi_server.py b/src/distutils2/tests/pypi_server.py
--- a/src/distutils2/tests/pypi_server.py
+++ b/src/distutils2/tests/pypi_server.py
@@ -26,10 +26,12 @@
         def wrapped(*args, **kwargs):
             server = PyPIServer(*server_args, **server_kwargs)
             server.start()
-            func(server=server, *args, **kwargs)
-            server.stop()
+            try:
+                func(server=server, *args, **kwargs)
+            finally:
+                server.stop()
         return wrapped
-    return wrapper 
+    return wrapper
 
 class PyPIServerTestCase(unittest.TestCase):
 
@@ -50,7 +52,7 @@
     """
 
     def __init__(self, test_static_path=None,
-            static_filesystem_paths=["default"], static_uri_paths=["simple"]):
+                 static_filesystem_paths=["default"], static_uri_paths=["simple"]):
         """Initialize the server.
 
         static_uri_paths and static_base_path are parameters used to provides
@@ -59,7 +61,7 @@
         """
         threading.Thread.__init__(self)
         self._run = True
-        self.httpd = HTTPServer(('', 0), PyPIRequestHandler) 
+        self.httpd = HTTPServer(('', 0), PyPIRequestHandler)
         self.httpd.RequestHandlerClass.log_request = lambda *_: None
         self.httpd.RequestHandlerClass.pypi_server = self
         self.address = (self.httpd.server_name, self.httpd.server_port)
@@ -131,7 +133,7 @@
         """
         # record the request. Read the input only on PUT or POST requests
         if self.command in ("PUT", "POST"):
-            if self.headers.dict.has_key("content-length"):
+            if 'content-length' in self.headers.dict:
                 request_data = self.rfile.read(
                     int(self.headers['content-length']))
             else:
@@ -158,7 +160,11 @@
                         relative_path += "index.html"
                     file = open(fs_path + relative_path)
                     data = file.read()
-                    self.make_response(data)
+                    if relative_path.endswith('.tar.gz'):
+                        headers=[('Content-type', 'application/x-gtar')]
+                    else:
+                        headers=[('Content-type', 'text/html')]
+                    self.make_response(data, headers=headers)
                 except IOError:
                     pass
 
@@ -171,8 +177,8 @@
             status, headers, data = self.pypi_server.get_next_response()
             self.make_response(data, status, headers)
 
-    def make_response(self, data, status=200, 
-            headers=[('Content-type', 'text/html')]):
+    def make_response(self, data, status=200,
+                      headers=[('Content-type', 'text/html')]):
         """Send the response to the HTTP client"""
         if not isinstance(status, int):
             try:
@@ -180,7 +186,7 @@
             except ValueError:
                 # we probably got something like YYY Codename. 
                 # Just get the first 3 digits
-                status = int(status[:3]) 
+                status = int(status[:3])
 
         self.send_response(status)
         for header, value in headers:
diff --git a/src/distutils2/tests/pypiserver/downloads_with_md5/simple/badmd5/badmd5-0.1.tar.gz b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/badmd5/badmd5-0.1.tar.gz
new file mode 100644
diff --git a/src/distutils2/tests/pypiserver/downloads_with_md5/simple/badmd5/index.html b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/badmd5/index.html
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/badmd5/index.html
@@ -0,0 +1,3 @@
+<html><body>
+<a href="badmd5-0.1.tar.gz#md5=3e3d86693d6564c807272b11b3069dfe" rel="download">badmd5-0.1.tar.gz</a><br/>
+</body></html>
diff --git a/src/distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/foobar-0.1.tar.gz b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/foobar-0.1.tar.gz
new file mode 100644
diff --git a/src/distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/index.html b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/index.html
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/index.html
@@ -0,0 +1,3 @@
+<html><body>
+<a href="foobar-0.1.tar.gz#md5=d41d8cd98f00b204e9800998ecf8427e" rel="download">foobar-0.1.tar.gz</a><br/>
+</body></html>
diff --git a/src/distutils2/tests/pypiserver/downloads_with_md5/simple/index.html b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/index.html
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/index.html
@@ -0,0 +1,2 @@
+<a href="foobar/">foobar/</a> 
+<a href="badmd5/">badmd5/</a> 
diff --git a/src/distutils2/tests/pypiserver/foo_bar_baz/simple/bar/index.html b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/bar/index.html
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/bar/index.html
@@ -0,0 +1,6 @@
+<html><head><title>Links for bar</title></head><body><h1>Links for bar</h1>
+<a rel="download" href="../../packages/source/F/bar/bar-1.0.tar.gz">bar-1.0.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/bar/bar-1.0.1.tar.gz">bar-1.0.1.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/bar/bar-2.0.tar.gz">bar-2.0.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/bar/bar-2.0.1.tar.gz">bar-2.0.1.tar.gz</a><br/> 
+</body></html>
diff --git a/src/distutils2/tests/pypiserver/foo_bar_baz/simple/baz/index.html b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/baz/index.html
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/baz/index.html
@@ -0,0 +1,6 @@
+<html><head><title>Links for baz</title></head><body><h1>Links for baz</h1>
+<a rel="download" href="../../packages/source/F/baz/baz-1.0.tar.gz">baz-1.0.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/baz/baz-1.0.1.tar.gz">baz-1.0.1.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/baz/baz-2.0.tar.gz">baz-2.0.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/baz/baz-2.0.1.tar.gz">baz-2.0.1.tar.gz</a><br/> 
+</body></html>
diff --git a/src/distutils2/tests/pypiserver/foo_bar_baz/simple/foo/index.html b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/foo/index.html
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/foo/index.html
@@ -0,0 +1,6 @@
+<html><head><title>Links for foo</title></head><body><h1>Links for foo</h1>
+<a rel="download" href="../../packages/source/F/foo/foo-1.0.tar.gz">foo-1.0.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/foo/foo-1.0.1.tar.gz">foo-1.0.1.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/foo/foo-2.0.tar.gz">foo-2.0.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/foo/foo-2.0.1.tar.gz">foo-2.0.1.tar.gz</a><br/> 
+</body></html>
diff --git a/src/distutils2/tests/pypiserver/foo_bar_baz/simple/index.html b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/index.html
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/index.html
@@ -0,0 +1,3 @@
+<a href="foo/">foo/</a> 
+<a href="bar/">bar/</a> 
+<a href="baz/">baz/</a> 
diff --git a/src/distutils2/tests/pypiserver/test_found_links/simple/foobar/index.html b/src/distutils2/tests/pypiserver/test_found_links/simple/foobar/index.html
--- a/src/distutils2/tests/pypiserver/test_found_links/simple/foobar/index.html
+++ b/src/distutils2/tests/pypiserver/test_found_links/simple/foobar/index.html
@@ -1,6 +1,6 @@
 <html><head><title>Links for Foobar</title></head><body><h1>Links for Foobar</h1>
-<a href="../../packages/source/F/Foobar/Foobar-1.0.tar.gz#md5=98fa833fdabcdd78d00245aead66c174">Foobar-1.0.tar.gz</a><br/> 
-<a href="../../packages/source/F/Foobar/Foobar-1.0.1.tar.gz#md5=2351efb20f6b7b5d9ce80fa4cb1bd9ca">Foobar-1.0.1.tar.gz</a><br/> 
-<a href="../../packages/source/F/Foobar/Foobar-2.0.tar.gz#md5=98fa833fdabcdd78d00245aead66c274">Foobar-2.0.tar.gz</a><br/> 
-<a href="../../packages/source/F/Foobar/Foobar-2.0.1.tar.gz#md5=2352efb20f6b7b5d9ce80fa4cb2bd9ca">Foobar-2.0.1.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/Foobar/Foobar-1.0.tar.gz#md5=98fa833fdabcdd78d00245aead66c174">Foobar-1.0.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/Foobar/Foobar-1.0.1.tar.gz#md5=2351efb20f6b7b5d9ce80fa4cb1bd9ca">Foobar-1.0.1.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/Foobar/Foobar-2.0.tar.gz#md5=98fa833fdabcdd78d00245aead66c274">Foobar-2.0.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/Foobar/Foobar-2.0.1.tar.gz#md5=2352efb20f6b7b5d9ce80fa4cb2bd9ca">Foobar-2.0.1.tar.gz</a><br/> 
 </body></html>
diff --git a/src/distutils2/tests/pypiserver/test_link_priority/external/external.html b/src/distutils2/tests/pypiserver/with_externals/external/external.html
rename from src/distutils2/tests/pypiserver/test_link_priority/external/external.html
rename to src/distutils2/tests/pypiserver/with_externals/external/external.html
diff --git a/src/distutils2/tests/pypiserver/test_link_priority/simple/foobar/index.html b/src/distutils2/tests/pypiserver/with_externals/simple/foobar/index.html
rename from src/distutils2/tests/pypiserver/test_link_priority/simple/foobar/index.html
rename to src/distutils2/tests/pypiserver/with_externals/simple/foobar/index.html
--- a/src/distutils2/tests/pypiserver/test_link_priority/simple/foobar/index.html
+++ b/src/distutils2/tests/pypiserver/with_externals/simple/foobar/index.html
@@ -1,4 +1,4 @@
 <html><body>
-<a href="/foobar-0.1.tar.gz#md5=0_correct_md5">foobar-0.1.tar.gz</a><br/>
+<a rel ="download" href="/foobar-0.1.tar.gz#md5=12345678901234567">foobar-0.1.tar.gz</a><br/>
 <a href="../../external/external.html" rel="homepage">external homepage</a><br/>
 </body></html>
diff --git a/src/distutils2/tests/pypiserver/test_link_priority/simple/index.html b/src/distutils2/tests/pypiserver/with_externals/simple/index.html
rename from src/distutils2/tests/pypiserver/test_link_priority/simple/index.html
rename to src/distutils2/tests/pypiserver/with_externals/simple/index.html
diff --git a/src/distutils2/tests/pypiserver/with_norel_links/external/homepage.html b/src/distutils2/tests/pypiserver/with_norel_links/external/homepage.html
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/pypiserver/with_norel_links/external/homepage.html
@@ -0,0 +1,7 @@
+<html>
+<body>
+<p>a rel=homepage HTML page</p>
+<a href="/foobar-2.0.tar.gz">foobar 2.0</a>
+</body>
+</html>
+
diff --git a/src/distutils2/tests/pypiserver/with_norel_links/external/nonrel.html b/src/distutils2/tests/pypiserver/with_norel_links/external/nonrel.html
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/pypiserver/with_norel_links/external/nonrel.html
@@ -0,0 +1,1 @@
+A page linked without rel="download" or rel="homepage" link.
diff --git a/src/distutils2/tests/pypiserver/with_norel_links/simple/foobar/index.html b/src/distutils2/tests/pypiserver/with_norel_links/simple/foobar/index.html
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/pypiserver/with_norel_links/simple/foobar/index.html
@@ -0,0 +1,6 @@
+<html><body>
+<a rel="download" href="/foobar-0.1.tar.gz" rel="download">foobar-0.1.tar.gz</a><br/>
+<a href="../../external/homepage.html" rel="homepage">external homepage</a><br/>
+<a href="../../external/nonrel.html">unrelated link</a><br/>
+<a href="/unrelated-0.2.tar.gz">unrelated download</a></br/>
+</body></html>
diff --git a/src/distutils2/tests/pypiserver/with_norel_links/simple/index.html b/src/distutils2/tests/pypiserver/with_norel_links/simple/index.html
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/pypiserver/with_norel_links/simple/index.html
@@ -0,0 +1,1 @@
+<a href="foobar/">foobar/</a> 
diff --git a/src/distutils2/tests/pypiserver/with_real_externals/simple/foobar/index.html b/src/distutils2/tests/pypiserver/with_real_externals/simple/foobar/index.html
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/pypiserver/with_real_externals/simple/foobar/index.html
@@ -0,0 +1,4 @@
+<html><body>
+<a rel="download" href="/foobar-0.1.tar.gz#md5=0_correct_md5">foobar-0.1.tar.gz</a><br/>
+<a href="http://a-really-external-website/external/external.html" rel="homepage">external homepage</a><br/>
+</body></html>
diff --git a/src/distutils2/tests/pypiserver/with_real_externals/simple/index.html b/src/distutils2/tests/pypiserver/with_real_externals/simple/index.html
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/pypiserver/with_real_externals/simple/index.html
@@ -0,0 +1,1 @@
+<a href="foobar/">foobar/</a> 
diff --git a/src/distutils2/tests/support.py b/src/distutils2/tests/support.py
--- a/src/distutils2/tests/support.py
+++ b/src/distutils2/tests/support.py
@@ -14,7 +14,6 @@
 
 from distutils2 import log
 from distutils2.log import DEBUG, INFO, WARN, ERROR, FATAL
-from distutils2.core import Distribution
 
 if sys.version_info >= (2, 7):
     # improved unittest package from 2.7's standard library
@@ -42,7 +41,7 @@
 
     def _log(self, level, msg, args):
         if level not in (DEBUG, INFO, WARN, ERROR, FATAL):
-            raise ValueError('%s wrong log level' % str(level))
+            raise ValueError('%s wrong log level' % level)
         self.logs.append((level, msg, args))
 
     def get_logs(self, *levels):
@@ -65,12 +64,22 @@
     def setUp(self):
         super(TempdirManager, self).setUp()
         self.tempdirs = []
+        self.tempfiles = []
 
     def tearDown(self):
         super(TempdirManager, self).tearDown()
         while self.tempdirs:
             d = self.tempdirs.pop()
             shutil.rmtree(d, os.name in ('nt', 'cygwin'))
+        for file_ in self.tempfiles:
+            if os.path.exists(file_):
+                os.remove(file_)
+
+    def mktempfile(self):
+        """Create a temporary file that will be cleaned up."""
+        tempfile_ = tempfile.NamedTemporaryFile()
+        self.tempfiles.append(tempfile_.name)
+        return tempfile_
 
     def mkdtemp(self):
         """Create a temporary directory that will be cleaned up.
@@ -105,6 +114,7 @@
         It returns the package directory and the distribution
         instance.
         """
+        from distutils2.dist import Distribution
         tmp_dir = self.mkdtemp()
         pkg_dir = os.path.join(tmp_dir, pkg_name)
         os.mkdir(pkg_dir)
diff --git a/src/distutils2/tests/test_Mixin2to3.py b/src/distutils2/tests/test_Mixin2to3.py
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/test_Mixin2to3.py
@@ -0,0 +1,54 @@
+"""Tests for distutils.command.build_py."""
+import sys
+import tempfile
+
+import distutils2
+from distutils2.tests import support
+from distutils2.tests.support import unittest
+from distutils2.command.build_py import Mixin2to3
+
+
+class Mixin2to3TestCase(support.TempdirManager, unittest.TestCase):
+
+    @unittest.skipUnless(sys.version > '2.6', 'Need >= 2.6')
+    def test_convert_code_only(self):
+        # used to check if code gets converted properly.
+        code_content = "print 'test'\n"
+        code_handle = self.mktempfile()
+        code_name = code_handle.name
+
+        code_handle.write(code_content)
+        code_handle.flush()
+
+        mixin2to3 = Mixin2to3()
+        mixin2to3._run_2to3([code_name])
+        converted_code_content = "print('test')\n"
+        new_code_content = "".join(open(code_name).readlines())
+
+        self.assertEquals(new_code_content, converted_code_content)
+
+    @unittest.skipUnless(sys.version > '2.6', 'Need >= 2.6')
+    def test_doctests_only(self):
+        # used to check if doctests gets converted properly.
+        doctest_content = '"""\n>>> print test\ntest\n"""\nprint test\n\n'
+        doctest_handle = self.mktempfile()
+        doctest_name = doctest_handle.name
+
+        doctest_handle.write(doctest_content)
+        doctest_handle.flush()
+
+        mixin2to3 = Mixin2to3()
+        mixin2to3._run_2to3([doctest_name])
+
+        converted_doctest_content = ['"""', '>>> print(test)', 'test', '"""',
+                                     'print(test)', '', '', '']
+        converted_doctest_content = '\n'.join(converted_doctest_content)
+        new_doctest_content = "".join(open(doctest_name).readlines())
+
+        self.assertEquals(new_doctest_content, converted_doctest_content)
+
+def test_suite():
+    return unittest.makeSuite(Mixin2to3TestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/src/distutils2/tests/test_bdist.py b/src/distutils2/tests/test_bdist.py
--- a/src/distutils2/tests/test_bdist.py
+++ b/src/distutils2/tests/test_bdist.py
@@ -1,8 +1,6 @@
 """Tests for distutils.command.bdist."""
 import sys
 import os
-import tempfile
-import shutil
 
 from distutils2.tests import run_unittest
 
@@ -10,8 +8,7 @@
 from distutils2.command.bdist import bdist
 from distutils2.tests import support
 from distutils2.tests.support import unittest
-from distutils2.spawn import find_executable
-from distutils2 import spawn
+from distutils2.util import find_executable
 from distutils2.errors import DistutilsExecError
 
 class BuildTestCase(support.TempdirManager,
@@ -25,7 +22,7 @@
         cmd = bdist(dist)
         cmd.formats = ['msi']
         cmd.ensure_finalized()
-        self.assertEquals(cmd.formats, ['msi'])
+        self.assertEqual(cmd.formats, ['msi'])
 
         # what format bdist offers ?
         # XXX an explicit list in bdist is
@@ -33,9 +30,9 @@
         # we should add a registry
         formats = ['zip', 'gztar', 'bztar', 'ztar', 'tar', 'wininst', 'msi']
         formats.sort()
-        founded = cmd.format_command.keys()
-        founded.sort()
-        self.assertEquals(founded, formats)
+        found = cmd.format_command.keys()
+        found.sort()
+        self.assertEqual(found, formats)
 
 def test_suite():
     return unittest.makeSuite(BuildTestCase)
diff --git a/src/distutils2/tests/test_bdist_dumb.py b/src/distutils2/tests/test_bdist_dumb.py
--- a/src/distutils2/tests/test_bdist_dumb.py
+++ b/src/distutils2/tests/test_bdist_dumb.py
@@ -78,7 +78,7 @@
             base = base.replace(':', '-')
 
         wanted = ['%s.zip' % base]
-        self.assertEquals(dist_created, wanted)
+        self.assertEqual(dist_created, wanted)
 
         # now let's check what we have in the zip file
         # XXX to be done
@@ -87,16 +87,16 @@
         pkg_dir, dist = self.create_dist()
         os.chdir(pkg_dir)
         cmd = bdist_dumb(dist)
-        self.assertEquals(cmd.bdist_dir, None)
+        self.assertEqual(cmd.bdist_dir, None)
         cmd.finalize_options()
 
         # bdist_dir is initialized to bdist_base/dumb if not set
         base = cmd.get_finalized_command('bdist').bdist_base
-        self.assertEquals(cmd.bdist_dir, os.path.join(base, 'dumb'))
+        self.assertEqual(cmd.bdist_dir, os.path.join(base, 'dumb'))
 
         # the format is set to a default value depending on the os.name
         default = cmd.default_format[os.name]
-        self.assertEquals(cmd.format, default)
+        self.assertEqual(cmd.format, default)
 
 def test_suite():
     return unittest.makeSuite(BuildDumbTestCase)
diff --git a/src/distutils2/tests/test_build.py b/src/distutils2/tests/test_build.py
--- a/src/distutils2/tests/test_build.py
+++ b/src/distutils2/tests/test_build.py
@@ -20,11 +20,11 @@
         cmd.finalize_options()
 
         # if not specified, plat_name gets the current platform
-        self.assertEquals(cmd.plat_name, get_platform())
+        self.assertEqual(cmd.plat_name, get_platform())
 
         # build_purelib is build + lib
         wanted = os.path.join(cmd.build_base, 'lib')
-        self.assertEquals(cmd.build_purelib, wanted)
+        self.assertEqual(cmd.build_purelib, wanted)
 
         # build_platlib is 'build/lib.platform-x.x[-pydebug]'
         # examples:
@@ -34,21 +34,21 @@
             self.assertTrue(cmd.build_platlib.endswith('-pydebug'))
             plat_spec += '-pydebug'
         wanted = os.path.join(cmd.build_base, 'lib' + plat_spec)
-        self.assertEquals(cmd.build_platlib, wanted)
+        self.assertEqual(cmd.build_platlib, wanted)
 
         # by default, build_lib = build_purelib
-        self.assertEquals(cmd.build_lib, cmd.build_purelib)
+        self.assertEqual(cmd.build_lib, cmd.build_purelib)
 
         # build_temp is build/temp.<plat>
         wanted = os.path.join(cmd.build_base, 'temp' + plat_spec)
-        self.assertEquals(cmd.build_temp, wanted)
+        self.assertEqual(cmd.build_temp, wanted)
 
         # build_scripts is build/scripts-x.x
         wanted = os.path.join(cmd.build_base, 'scripts-' +  sys.version[0:3])
-        self.assertEquals(cmd.build_scripts, wanted)
+        self.assertEqual(cmd.build_scripts, wanted)
 
         # executable is os.path.normpath(sys.executable)
-        self.assertEquals(cmd.executable, os.path.normpath(sys.executable))
+        self.assertEqual(cmd.executable, os.path.normpath(sys.executable))
 
 def test_suite():
     return unittest.makeSuite(BuildTestCase)
diff --git a/src/distutils2/tests/test_build_clib.py b/src/distutils2/tests/test_build_clib.py
--- a/src/distutils2/tests/test_build_clib.py
+++ b/src/distutils2/tests/test_build_clib.py
@@ -5,7 +5,7 @@
 from distutils2.command.build_clib import build_clib
 from distutils2.errors import DistutilsSetupError
 from distutils2.tests import support
-from distutils2.spawn import find_executable
+from distutils2.util import find_executable
 from distutils2.tests.support import unittest
 
 class BuildCLibTestCase(support.TempdirManager,
@@ -55,14 +55,14 @@
         self.assertRaises(DistutilsSetupError, cmd.get_source_files)
 
         cmd.libraries = [('name', {'sources': ['a', 'b']})]
-        self.assertEquals(cmd.get_source_files(), ['a', 'b'])
+        self.assertEqual(cmd.get_source_files(), ['a', 'b'])
 
         cmd.libraries = [('name', {'sources': ('a', 'b')})]
-        self.assertEquals(cmd.get_source_files(), ['a', 'b'])
+        self.assertEqual(cmd.get_source_files(), ['a', 'b'])
 
         cmd.libraries = [('name', {'sources': ('a', 'b')}),
                          ('name2', {'sources': ['c', 'd']})]
-        self.assertEquals(cmd.get_source_files(), ['a', 'b', 'c', 'd'])
+        self.assertEqual(cmd.get_source_files(), ['a', 'b', 'c', 'd'])
 
     def test_build_libraries(self):
 
@@ -91,11 +91,11 @@
 
         cmd.include_dirs = 'one-dir'
         cmd.finalize_options()
-        self.assertEquals(cmd.include_dirs, ['one-dir'])
+        self.assertEqual(cmd.include_dirs, ['one-dir'])
 
         cmd.include_dirs = None
         cmd.finalize_options()
-        self.assertEquals(cmd.include_dirs, [])
+        self.assertEqual(cmd.include_dirs, [])
 
         cmd.distribution.libraries = 'WONTWORK'
         self.assertRaises(DistutilsSetupError, cmd.finalize_options)
diff --git a/src/distutils2/tests/test_build_ext.py b/src/distutils2/tests/test_build_ext.py
--- a/src/distutils2/tests/test_build_ext.py
+++ b/src/distutils2/tests/test_build_ext.py
@@ -1,6 +1,5 @@
 import sys
 import os
-import tempfile
 import shutil
 from StringIO import StringIO
 import warnings
@@ -81,11 +80,11 @@
         for attr in ('error', 'foo', 'new', 'roj'):
             self.assertTrue(hasattr(xx, attr))
 
-        self.assertEquals(xx.foo(2, 5), 7)
-        self.assertEquals(xx.foo(13,15), 28)
-        self.assertEquals(xx.new().demo(), None)
+        self.assertEqual(xx.foo(2, 5), 7)
+        self.assertEqual(xx.foo(13,15), 28)
+        self.assertEqual(xx.new().demo(), None)
         doc = 'This is a template module just for instruction.'
-        self.assertEquals(xx.__doc__, doc)
+        self.assertEqual(xx.__doc__, doc)
         self.assertTrue(isinstance(xx.Null(), xx.Null))
         self.assertTrue(isinstance(xx.Str(), xx.Str))
 
@@ -195,7 +194,7 @@
         cmd = build_ext(dist)
         cmd.libraries = 'my_lib'
         cmd.finalize_options()
-        self.assertEquals(cmd.libraries, ['my_lib'])
+        self.assertEqual(cmd.libraries, ['my_lib'])
 
         # make sure cmd.library_dirs is turned into a list
         # if it's a string
@@ -209,7 +208,7 @@
         cmd = build_ext(dist)
         cmd.rpath = os.pathsep.join(['one', 'two'])
         cmd.finalize_options()
-        self.assertEquals(cmd.rpath, ['one', 'two'])
+        self.assertEqual(cmd.rpath, ['one', 'two'])
 
         # XXX more tests to perform for win32
 
@@ -218,79 +217,32 @@
         cmd = build_ext(dist)
         cmd.define = 'one,two'
         cmd.finalize_options()
-        self.assertEquals(cmd.define, [('one', '1'), ('two', '1')])
+        self.assertEqual(cmd.define, [('one', '1'), ('two', '1')])
 
         # make sure undef is turned into a list of
         # strings if they are ','-separated strings
         cmd = build_ext(dist)
         cmd.undef = 'one,two'
         cmd.finalize_options()
-        self.assertEquals(cmd.undef, ['one', 'two'])
+        self.assertEqual(cmd.undef, ['one', 'two'])
 
         # make sure swig_opts is turned into a list
         cmd = build_ext(dist)
         cmd.swig_opts = None
         cmd.finalize_options()
-        self.assertEquals(cmd.swig_opts, [])
+        self.assertEqual(cmd.swig_opts, [])
 
         cmd = build_ext(dist)
         cmd.swig_opts = '1 2'
         cmd.finalize_options()
-        self.assertEquals(cmd.swig_opts, ['1', '2'])
-
-    def test_check_extensions_list(self):
-        dist = Distribution()
-        cmd = build_ext(dist)
-        cmd.finalize_options()
-
-        #'extensions' option must be a list of Extension instances
-        self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, 'foo')
-
-        # each element of 'ext_modules' option must be an
-        # Extension instance or 2-tuple
-        exts = [('bar', 'foo', 'bar'), 'foo']
-        self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
-
-        # first element of each tuple in 'ext_modules'
-        # must be the extension name (a string) and match
-        # a python dotted-separated name
-        exts = [('foo-bar', '')]
-        self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
-
-        # second element of each tuple in 'ext_modules'
-        # must be a ary (build info)
-        exts = [('foo.bar', '')]
-        self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
-
-        # ok this one should pass
-        exts = [('foo.bar', {'sources': [''], 'libraries': 'foo',
-                             'some': 'bar'})]
-        cmd.check_extensions_list(exts)
-        ext = exts[0]
-        self.assertTrue(isinstance(ext, Extension))
-
-        # check_extensions_list adds in ext the values passed
-        # when they are in ('include_dirs', 'library_dirs', 'libraries'
-        # 'extra_objects', 'extra_compile_args', 'extra_link_args')
-        self.assertEquals(ext.libraries, 'foo')
-        self.assertTrue(not hasattr(ext, 'some'))
-
-        # 'macros' element of build info dict must be 1- or 2-tuple
-        exts = [('foo.bar', {'sources': [''], 'libraries': 'foo',
-                'some': 'bar', 'macros': [('1', '2', '3'), 'foo']})]
-        self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
-
-        exts[0][1]['macros'] = [('1', '2'), ('3',)]
-        cmd.check_extensions_list(exts)
-        self.assertEquals(exts[0].undef_macros, ['3'])
-        self.assertEquals(exts[0].define_macros, [('1', '2')])
+        self.assertEqual(cmd.swig_opts, ['1', '2'])
 
     def test_get_source_files(self):
         modules = [Extension('foo', ['xxx'], optional=False)]
         dist = Distribution({'name': 'xx', 'ext_modules': modules})
         cmd = build_ext(dist)
         cmd.ensure_finalized()
-        self.assertEquals(cmd.get_source_files(), ['xxx'])
+        self.assertEqual(cmd.get_source_files(), ['xxx'])
 
     def test_compiler_option(self):
         # cmd.compiler is an option and
@@ -301,7 +253,7 @@
         cmd.compiler = 'unix'
         cmd.ensure_finalized()
         cmd.run()
-        self.assertEquals(cmd.compiler, 'unix')
+        self.assertEqual(cmd.compiler, 'unix')
 
     def test_get_outputs(self):
         tmp_dir = self.mkdtemp()
@@ -312,7 +264,7 @@
                              'ext_modules': [ext]})
         cmd = build_ext(dist)
         cmd.ensure_finalized()
-        self.assertEquals(len(cmd.get_outputs()), 1)
+        self.assertEqual(len(cmd.get_outputs()), 1)
 
         if os.name == "nt":
             cmd.debug = sys.executable.endswith("_d.exe")
@@ -332,19 +284,19 @@
         finally:
             os.chdir(old_wd)
         self.assertTrue(os.path.exists(so_file))
-        self.assertEquals(os.path.splitext(so_file)[-1],
+        self.assertEqual(os.path.splitext(so_file)[-1],
                           sysconfig.get_config_var('SO'))
         so_dir = os.path.dirname(so_file)
-        self.assertEquals(so_dir, other_tmp_dir)
+        self.assertEqual(so_dir, other_tmp_dir)
 
         cmd.inplace = 0
         cmd.run()
         so_file = cmd.get_outputs()[0]
         self.assertTrue(os.path.exists(so_file))
-        self.assertEquals(os.path.splitext(so_file)[-1],
+        self.assertEqual(os.path.splitext(so_file)[-1],
                           sysconfig.get_config_var('SO'))
         so_dir = os.path.dirname(so_file)
-        self.assertEquals(so_dir, cmd.build_lib)
+        self.assertEqual(so_dir, cmd.build_lib)
 
         # inplace = 0, cmd.package = 'bar'
         build_py = cmd.get_finalized_command('build_py')
@@ -352,7 +304,7 @@
         path = cmd.get_ext_fullpath('foo')
         # checking that the last directory is the build_dir
         path = os.path.split(path)[0]
-        self.assertEquals(path, cmd.build_lib)
+        self.assertEqual(path, cmd.build_lib)
 
         # inplace = 1, cmd.package = 'bar'
         cmd.inplace = 1
@@ -366,7 +318,7 @@
         # checking that the last directory is bar
         path = os.path.split(path)[0]
         lastdir = os.path.split(path)[-1]
-        self.assertEquals(lastdir, 'bar')
+        self.assertEqual(lastdir, 'bar')
 
     def test_ext_fullpath(self):
         ext = sysconfig.get_config_vars()['SO']
@@ -382,14 +334,14 @@
         curdir = os.getcwd()
         wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext)
         path = cmd.get_ext_fullpath('lxml.etree')
-        self.assertEquals(wanted, path)
+        self.assertEqual(wanted, path)
 
         # building lxml.etree not inplace
         cmd.inplace = 0
         cmd.build_lib = os.path.join(curdir, 'tmpdir')
         wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext)
         path = cmd.get_ext_fullpath('lxml.etree')
-        self.assertEquals(wanted, path)
+        self.assertEqual(wanted, path)
 
         # building twisted.runner.portmap not inplace
         build_py = cmd.get_finalized_command('build_py')
@@ -398,13 +350,13 @@
         path = cmd.get_ext_fullpath('twisted.runner.portmap')
         wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner',
                               'portmap' + ext)
-        self.assertEquals(wanted, path)
+        self.assertEqual(wanted, path)
 
         # building twisted.runner.portmap inplace
         cmd.inplace = 1
         path = cmd.get_ext_fullpath('twisted.runner.portmap')
         wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext)
-        self.assertEquals(wanted, path)
+        self.assertEqual(wanted, path)
 
 def test_suite():
     src = _get_source_filename()
@@ -416,4 +368,4 @@
     else: return unittest.makeSuite(BuildExtTestCase)
 
 if __name__ == '__main__':
-    distsutils2.tests.run_unittest(test_suite())
+    distutils2.tests.run_unittest(test_suite())
diff --git a/src/distutils2/tests/test_build_py.py b/src/distutils2/tests/test_build_py.py
--- a/src/distutils2/tests/test_build_py.py
+++ b/src/distutils2/tests/test_build_py.py
@@ -19,11 +19,15 @@
     def test_package_data(self):
         sources = self.mkdtemp()
         f = open(os.path.join(sources, "__init__.py"), "w")
-        f.write("# Pretend this is a package.")
-        f.close()
+        try:
+            f.write("# Pretend this is a package.")
+        finally:
+            f.close()
         f = open(os.path.join(sources, "README.txt"), "w")
-        f.write("Info about this package")
-        f.close()
+        try:
+            f.write("Info about this package")
+        finally:
+            f.close()
 
         destination = self.mkdtemp()
 
diff --git a/src/distutils2/tests/test_build_scripts.py b/src/distutils2/tests/test_build_scripts.py
--- a/src/distutils2/tests/test_build_scripts.py
+++ b/src/distutils2/tests/test_build_scripts.py
@@ -74,8 +74,10 @@
 
     def write_script(self, dir, name, text):
         f = open(os.path.join(dir, name), "w")
-        f.write(text)
-        f.close()
+        try:
+            f.write(text)
+        finally:
+            f.close()
 
     def test_version_int(self):
         source = self.mkdtemp()
diff --git a/src/distutils2/tests/test_ccompiler.py b/src/distutils2/tests/test_ccompiler.py
--- a/src/distutils2/tests/test_ccompiler.py
+++ b/src/distutils2/tests/test_ccompiler.py
@@ -31,7 +31,7 @@
         opts = gen_lib_options(compiler, libdirs, runlibdirs, libs)
         wanted = ['-Llib1', '-Llib2', '-cool', '-Rrunlib1', 'found',
                   '-lname2']
-        self.assertEquals(opts, wanted)
+        self.assertEqual(opts, wanted)
 
     def test_customize_compiler(self):
 
@@ -51,7 +51,7 @@
 
         comp = compiler()
         customize_compiler(comp)
-        self.assertEquals(comp.exes['archiver'], 'my_ar -arflags')
+        self.assertEqual(comp.exes['archiver'], 'my_ar -arflags')
 
 def test_suite():
     return unittest.makeSuite(CCompilerTestCase)
diff --git a/src/distutils2/tests/test_check.py b/src/distutils2/tests/test_check.py
--- a/src/distutils2/tests/test_check.py
+++ b/src/distutils2/tests/test_check.py
@@ -37,7 +37,7 @@
                     'name': 'xxx', 'version': 'xxx'
                     }
         cmd = self._run(metadata)
-        self.assertEquals(len(cmd._warnings), 0)
+        self.assertEqual(len(cmd._warnings), 0)
 
         # now with the strict mode, we should
         # get an error if there are missing metadata
@@ -45,7 +45,7 @@
 
         # and of course, no error when all metadata are present
         cmd = self._run(metadata, strict=1)
-        self.assertEquals(len(cmd._warnings), 0)
+        self.assertEqual(len(cmd._warnings), 0)
 
     def test_check_restructuredtext(self):
         if not _HAS_DOCUTILS: # won't test without docutils
@@ -55,7 +55,7 @@
         pkg_info, dist = self.create_dist(description=broken_rest)
         cmd = check(dist)
         cmd.check_restructuredtext()
-        self.assertEquals(len(cmd._warnings), 1)
+        self.assertEqual(len(cmd._warnings), 1)
 
         # let's see if we have an error with strict=1
         metadata = {'home_page': 'xxx', 'author': 'xxx',
@@ -69,7 +69,7 @@
         # and non-broken rest
         metadata['description'] = 'title\n=====\n\ntest'
         cmd = self._run(metadata, strict=1, restructuredtext=1)
-        self.assertEquals(len(cmd._warnings), 0)
+        self.assertEqual(len(cmd._warnings), 0)
 
     def test_check_all(self):
 
diff --git a/src/distutils2/tests/test_cmd.py b/src/distutils2/tests/test_cmd.py
--- a/src/distutils2/tests/test_cmd.py
+++ b/src/distutils2/tests/test_cmd.py
@@ -43,7 +43,7 @@
 
         # making sure execute gets called properly
         def _execute(func, args, exec_msg, level):
-            self.assertEquals(exec_msg, 'generating out from in')
+            self.assertEqual(exec_msg, 'generating out from in')
         cmd.force = True
         cmd.execute = _execute
         cmd.make_file(infiles='in', outfile='out', func='func', args=())
@@ -62,7 +62,7 @@
 
         wanted = ["command options for 'MyCmd':", '  option1 = 1',
                   '  option2 = 1']
-        self.assertEquals(msgs, wanted)
+        self.assertEqual(msgs, wanted)
 
     def test_ensure_string(self):
         cmd = self.cmd
@@ -80,7 +80,7 @@
         cmd = self.cmd
         cmd.option1 = 'ok,dok'
         cmd.ensure_string_list('option1')
-        self.assertEquals(cmd.option1, ['ok', 'dok'])
+        self.assertEqual(cmd.option1, ['ok', 'dok'])
 
         cmd.option2 = ['xxx', 'www']
         cmd.ensure_string_list('option2')
diff --git a/src/distutils2/tests/test_config.py b/src/distutils2/tests/test_config.py
--- a/src/distutils2/tests/test_config.py
+++ b/src/distutils2/tests/test_config.py
@@ -1,7 +1,6 @@
 """Tests for distutils.pypirc.pypirc."""
 import sys
 import os
-import tempfile
 import shutil
 
 from distutils2.core import PyPIRCCommand
@@ -87,20 +86,20 @@
 
         config = config.items()
         config.sort()
-        waited = [('password', 'secret'), ('realm', 'pypi'),
-                  ('repository', 'http://pypi.python.org/pypi'),
-                  ('server', 'server1'), ('username', 'me')]
-        self.assertEquals(config, waited)
+        expected = [('password', 'secret'), ('realm', 'pypi'),
+                    ('repository', 'http://pypi.python.org/pypi'),
+                    ('server', 'server1'), ('username', 'me')]
+        self.assertEqual(config, expected)
 
         # old format
         self.write_file(self.rc, PYPIRC_OLD)
         config = cmd._read_pypirc()
         config = config.items()
         config.sort()
-        waited = [('password', 'secret'), ('realm', 'pypi'),
-                  ('repository', 'http://pypi.python.org/pypi'),
-                  ('server', 'server-login'), ('username', 'tarek')]
-        self.assertEquals(config, waited)
+        expected = [('password', 'secret'), ('realm', 'pypi'),
+                    ('repository', 'http://pypi.python.org/pypi'),
+                    ('server', 'server-login'), ('username', 'tarek')]
+        self.assertEqual(config, expected)
 
     def test_server_empty_registration(self):
         cmd = self._cmd(self.dist)
@@ -109,7 +108,7 @@
         cmd._store_pypirc('tarek', 'xxx')
         self.assertTrue(os.path.exists(rc))
         content = open(rc).read()
-        self.assertEquals(content, WANTED)
+        self.assertEqual(content, WANTED)
 
 def test_suite():
     return unittest.makeSuite(PyPIRCCommandTestCase)
diff --git a/src/distutils2/tests/test_config_cmd.py b/src/distutils2/tests/test_config_cmd.py
--- a/src/distutils2/tests/test_config_cmd.py
+++ b/src/distutils2/tests/test_config_cmd.py
@@ -34,7 +34,7 @@
             f.close()
 
         dump_file(this_file, 'I am the header')
-        self.assertEquals(len(self._logs), numlines+1)
+        self.assertEqual(len(self._logs), numlines+1)
 
     def test_search_cpp(self):
         if sys.platform == 'win32':
@@ -44,10 +44,10 @@
 
         # simple pattern searches
         match = cmd.search_cpp(pattern='xxx', body='// xxx')
-        self.assertEquals(match, 0)
+        self.assertEqual(match, 0)
 
         match = cmd.search_cpp(pattern='_configtest', body='// xxx')
-        self.assertEquals(match, 1)
+        self.assertEqual(match, 1)
 
     def test_finalize_options(self):
         # finalize_options does a bit of transformation
@@ -59,9 +59,9 @@
         cmd.library_dirs = 'three%sfour' % os.pathsep
         cmd.ensure_finalized()
 
-        self.assertEquals(cmd.include_dirs, ['one', 'two'])
-        self.assertEquals(cmd.libraries, ['one'])
-        self.assertEquals(cmd.library_dirs, ['three', 'four'])
+        self.assertEqual(cmd.include_dirs, ['one', 'two'])
+        self.assertEqual(cmd.libraries, ['one'])
+        self.assertEqual(cmd.library_dirs, ['three', 'four'])
 
     def test_clean(self):
         # _clean removes files
diff --git a/src/distutils2/tests/test_converter.py b/src/distutils2/tests/test_converter.py
--- a/src/distutils2/tests/test_converter.py
+++ b/src/distutils2/tests/test_converter.py
@@ -30,7 +30,7 @@
             wanted = file_.replace('before', 'after')
             wanted = _read_file(os.path.join(convdir, wanted))
             res = ref.refactor_string(original, 'setup.py')
-            self.assertEquals(str(res), wanted)
+            self.assertEqual(str(res), wanted)
 
 def test_suite():
     return unittest.makeSuite(ConverterTestCase)
diff --git a/src/distutils2/tests/test_cygwinccompiler.py b/src/distutils2/tests/test_cygwinccompiler.py
--- a/src/distutils2/tests/test_cygwinccompiler.py
+++ b/src/distutils2/tests/test_cygwinccompiler.py
@@ -44,48 +44,48 @@
         sys.version = ('2.6.1 (r261:67515, Dec  6 2008, 16:42:21) \n[GCC '
                        '4.0.1 (Apple Computer, Inc. build 5370)]')
 
-        self.assertEquals(check_config_h()[0], CONFIG_H_OK)
+        self.assertEqual(check_config_h()[0], CONFIG_H_OK)
 
         # then it tries to see if it can find "__GNUC__" in pyconfig.h
         sys.version = 'something without the *CC word'
 
         # if the file doesn't exist it returns  CONFIG_H_UNCERTAIN
-        self.assertEquals(check_config_h()[0], CONFIG_H_UNCERTAIN)
+        self.assertEqual(check_config_h()[0], CONFIG_H_UNCERTAIN)
 
         # if it exists but does not contain __GNUC__, it returns CONFIG_H_NOTOK
         self.write_file(self.python_h, 'xxx')
-        self.assertEquals(check_config_h()[0], CONFIG_H_NOTOK)
+        self.assertEqual(check_config_h()[0], CONFIG_H_NOTOK)
 
         # and CONFIG_H_OK if __GNUC__ is found
         self.write_file(self.python_h, 'xxx __GNUC__ xxx')
-        self.assertEquals(check_config_h()[0], CONFIG_H_OK)
+        self.assertEqual(check_config_h()[0], CONFIG_H_OK)
 
     def test_get_msvcr(self):
 
         # none
         sys.version  = ('2.6.1 (r261:67515, Dec  6 2008, 16:42:21) '
                         '\n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]')
-        self.assertEquals(get_msvcr(), None)
+        self.assertEqual(get_msvcr(), None)
 
         # MSVC 7.0
         sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) '
                        '[MSC v.1300 32 bits (Intel)]')
-        self.assertEquals(get_msvcr(), ['msvcr70'])
+        self.assertEqual(get_msvcr(), ['msvcr70'])
 
         # MSVC 7.1
         sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) '
                        '[MSC v.1310 32 bits (Intel)]')
-        self.assertEquals(get_msvcr(), ['msvcr71'])
+        self.assertEqual(get_msvcr(), ['msvcr71'])
 
         # VS2005 / MSVC 8.0
         sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) '
                        '[MSC v.1400 32 bits (Intel)]')
-        self.assertEquals(get_msvcr(), ['msvcr80'])
+        self.assertEqual(get_msvcr(), ['msvcr80'])
 
         # VS2008 / MSVC 9.0
         sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) '
                        '[MSC v.1500 32 bits (Intel)]')
-        self.assertEquals(get_msvcr(), ['msvcr90'])
+        self.assertEqual(get_msvcr(), ['msvcr90'])
 
         # unknown
         sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) '
diff --git a/src/distutils2/tests/test_depgraph.py b/src/distutils2/tests/test_depgraph.py
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/test_depgraph.py
@@ -0,0 +1,187 @@
+"""Tests for distutils.depgraph """
+
+from distutils2.tests import support
+from distutils2.tests.support import unittest
+from distutils2 import depgraph
+from distutils2._backport import pkgutil
+
+import os
+import sys
+import re
+try:
+    import cStringIO as StringIO
+except ImportError:
+    import StringIO
+
+class DepGraphTestCase(support.LoggingSilencer,
+                       unittest.TestCase):
+
+    DISTROS_DIST = ('choxie', 'grammar', 'towel-stuff')
+    DISTROS_EGG  = ('bacon', 'banana', 'strawberry', 'cheese')
+    EDGE = re.compile(
+           r'"(?P<from>.*)" -> "(?P<to>.*)" \[label="(?P<label>.*)"\]'
+           )
+
+    def checkLists(self, l1, l2):
+        """ Compare two lists without taking the order into consideration """
+        self.assertListEqual(sorted(l1), sorted(l2))
+
+    def setUp(self):
+        super(DepGraphTestCase, self).setUp()
+        path = os.path.join(os.path.dirname(__file__), '..', '_backport',
+                            'tests', 'fake_dists')
+        path = os.path.abspath(path)
+        self.sys_path = sys.path[:]
+        sys.path[0:0] = [path]
+
+    def test_generate_graph(self):
+        dists = []
+        for name in self.DISTROS_DIST:
+            dist = pkgutil.get_distribution(name)
+            self.assertNotEqual(dist, None)
+            dists.append(dist)
+
+        choxie, grammar, towel = dists
+
+        graph = depgraph.generate_graph(dists)
+
+        deps = [(x.name, y) for (x,y) in graph.adjacency_list[choxie]]
+        self.checkLists([('towel-stuff', 'towel-stuff (0.1)')], deps)
+        self.assertTrue(choxie in graph.reverse_list[towel])
+        self.checkLists(graph.missing[choxie], [])
+
+        deps = [(x.name, y) for (x,y) in graph.adjacency_list[grammar]]
+        self.checkLists([], deps)
+        self.checkLists(graph.missing[grammar], ['truffles (>=1.2)'])
+
+        deps = [(x.name, y) for (x,y) in graph.adjacency_list[towel]]
+        self.checkLists([], deps)
+        self.checkLists(graph.missing[towel], ['bacon (<=0.2)'])
+
+    def test_generate_graph_egg(self):
+        dists = []
+        for name in self.DISTROS_DIST + self.DISTROS_EGG:
+            dist = pkgutil.get_distribution(name, use_egg_info=True)
+            self.assertNotEqual(dist, None)
+            dists.append(dist)
+
+        choxie, grammar, towel, bacon, banana, strawberry, cheese = dists
+
+        graph = depgraph.generate_graph(dists)
+
+        deps = [(x.name, y) for (x,y) in graph.adjacency_list[choxie]]
+        self.checkLists([('towel-stuff', 'towel-stuff (0.1)')], deps)
+        self.assertTrue(choxie in graph.reverse_list[towel])
+        self.checkLists(graph.missing[choxie], [])
+
+        deps = [(x.name, y) for (x,y) in graph.adjacency_list[grammar]]
+        self.checkLists([('bacon', 'truffles (>=1.2)')], deps)
+        self.checkLists(graph.missing[grammar], [])
+        self.assertTrue(grammar in graph.reverse_list[bacon])
+
+        deps = [(x.name, y) for (x,y) in graph.adjacency_list[towel]]
+        self.checkLists([('bacon', 'bacon (<=0.2)')], deps)
+        self.checkLists(graph.missing[towel], [])
+        self.assertTrue(towel in graph.reverse_list[bacon])
+
+        deps = [(x.name, y) for (x,y) in graph.adjacency_list[bacon]]
+        self.checkLists([], deps)
+        self.checkLists(graph.missing[bacon], [])
+
+        deps = [(x.name, y) for (x,y) in graph.adjacency_list[banana]]
+        self.checkLists([('strawberry', 'strawberry (>=0.5)')], deps)
+        self.checkLists(graph.missing[banana], [])
+        self.assertTrue(banana in graph.reverse_list[strawberry])
+
+        deps = [(x.name, y) for (x,y) in graph.adjacency_list[strawberry]]
+        self.checkLists([], deps)
+        self.checkLists(graph.missing[strawberry], [])
+
+        deps = [(x.name, y) for (x,y) in graph.adjacency_list[cheese]]
+        self.checkLists([], deps)
+        self.checkLists(graph.missing[cheese], [])
+
+    def test_dependent_dists(self):
+        dists = []
+        for name in self.DISTROS_DIST:
+            dist = pkgutil.get_distribution(name)
+            self.assertNotEqual(dist, None)
+            dists.append(dist)
+
+        choxie, grammar, towel = dists
+
+        deps = [d.name for d in depgraph.dependent_dists(dists, choxie)]
+        self.checkLists([], deps)
+
+        deps = [d.name for d in depgraph.dependent_dists(dists, grammar)]
+        self.checkLists([], deps)
+
+        deps = [d.name for d in depgraph.dependent_dists(dists, towel)]
+        self.checkLists(['choxie'], deps)
+
+
+    def test_dependent_dists_egg(self):
+        dists = []
+        for name in self.DISTROS_DIST + self.DISTROS_EGG:
+            dist = pkgutil.get_distribution(name, use_egg_info=True)
+            self.assertNotEqual(dist, None)
+            dists.append(dist)
+
+        choxie, grammar, towel, bacon, banana, strawberry, cheese = dists
+
+        deps = [d.name for d in depgraph.dependent_dists(dists, choxie)]
+        self.checkLists([], deps)
+
+        deps = [d.name for d in depgraph.dependent_dists(dists, grammar)]
+        self.checkLists([], deps)
+
+        deps = [d.name for d in depgraph.dependent_dists(dists, towel)]
+        self.checkLists(['choxie'], deps)
+
+        deps = [d.name for d in depgraph.dependent_dists(dists, bacon)]
+        self.checkLists(['choxie', 'towel-stuff', 'grammar'], deps)
+
+        deps = [d.name for d in depgraph.dependent_dists(dists, strawberry)]
+        self.checkLists(['banana'], deps)
+
+        deps = [d.name for d in depgraph.dependent_dists(dists, cheese)]
+        self.checkLists([], deps)
+
+    def test_graph_to_dot(self):
+        expected = (
+            ('towel-stuff', 'bacon', 'bacon (<=0.2)'),
+            ('grammar', 'bacon', 'truffles (>=1.2)'),
+            ('choxie', 'towel-stuff', 'towel-stuff (0.1)'),
+            ('banana', 'strawberry', 'strawberry (>=0.5)')
+        )
+
+        dists = []
+        for name in self.DISTROS_DIST + self.DISTROS_EGG:
+            dist = pkgutil.get_distribution(name, use_egg_info=True)
+            self.assertNotEqual(dist, None)
+            dists.append(dist)
+
+        graph = depgraph.generate_graph(dists)
+        buf = StringIO.StringIO()
+        depgraph.graph_to_dot(graph, buf)
+        buf.seek(0)
+        matches = []
+        lines = buf.readlines()
+        for line in lines[1:-1]: # skip the first and the last lines
+            if line[-1] == '\n':
+                line = line[:-1]
+            match = self.EDGE.match(line.strip())
+            self.assertTrue(match is not None)
+            matches.append(match.groups())
+
+        self.checkLists(matches, expected)
+
+    def tearDown(self):
+        super(DepGraphTestCase, self).tearDown()
+        sys.path = self.sys_path
+
+def test_suite():
+    return unittest.makeSuite(DepGraphTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/src/distutils2/tests/test_dist.py b/src/distutils2/tests/test_dist.py
--- a/src/distutils2/tests/test_dist.py
+++ b/src/distutils2/tests/test_dist.py
@@ -71,11 +71,11 @@
         sys.argv.append("build")
 
         __, stdout = captured_stdout(self.create_distribution, files)
-        self.assertEquals(stdout, '')
+        self.assertEqual(stdout, '')
         distutils2.dist.DEBUG = True
         try:
             __, stdout = captured_stdout(self.create_distribution, files)
-            self.assertEquals(stdout, '')
+            self.assertEqual(stdout, '')
         finally:
             distutils2.dist.DEBUG = False
 
@@ -129,13 +129,13 @@
         # Check DistributionMetadata handling of Unicode fields
         tmp_dir = self.mkdtemp()
         my_file = os.path.join(tmp_dir, 'f')
-        klass = Distribution
+        cls = Distribution
 
-        dist = klass(attrs={'author': u'Mister Café',
-                            'name': 'my.package',
-                            'maintainer': u'Café Junior',
-                            'summary': u'Café torréfié',
-                            'description': u'Héhéhé'})
+        dist = cls(attrs={'author': u'Mister Café',
+                          'name': 'my.package',
+                          'maintainer': u'Café Junior',
+                          'summary': u'Café torréfié',
+                          'description': u'Héhéhé'})
 
 
         # let's make sure the file can be written
@@ -144,11 +144,11 @@
         dist.metadata.write_file(open(my_file, 'w'))
 
         # regular ascii is of course always usable
-        dist = klass(attrs={'author': 'Mister Cafe',
-                            'name': 'my.package',
-                            'maintainer': 'Cafe Junior',
-                            'summary': 'Cafe torrefie',
-                            'description': 'Hehehe'})
+        dist = cls(attrs={'author': 'Mister Cafe',
+                          'name': 'my.package',
+                          'maintainer': 'Cafe Junior',
+                          'summary': 'Cafe torrefie',
+                          'description': 'Hehehe'})
 
         my_file2 = os.path.join(tmp_dir, 'f2')
         dist.metadata.write_file(open(my_file, 'w'))
@@ -156,7 +156,7 @@
     def test_empty_options(self):
         # an empty options dictionary should not stay in the
         # list of attributes
-        klass = Distribution
+        cls = Distribution
 
         # catching warnings
         warns = []
@@ -166,15 +166,15 @@
         old_warn = warnings.warn
         warnings.warn = _warn
         try:
-            dist = klass(attrs={'author': 'xxx',
-                                'name': 'xxx',
-                                'version': 'xxx',
-                                'url': 'xxxx',
-                                'options': {}})
+            dist = cls(attrs={'author': 'xxx',
+                              'name': 'xxx',
+                              'version': 'xxx',
+                              'url': 'xxxx',
+                              'options': {}})
         finally:
             warnings.warn = old_warn
 
-        self.assertEquals(len(warns), 0)
+        self.assertEqual(len(warns), 0)
 
     def test_finalize_options(self):
 
@@ -185,20 +185,20 @@
         dist.finalize_options()
 
         # finalize_option splits platforms and keywords
-        self.assertEquals(dist.metadata['platform'], ['one', 'two'])
-        self.assertEquals(dist.metadata['keywords'], ['one', 'two'])
+        self.assertEqual(dist.metadata['platform'], ['one', 'two'])
+        self.assertEqual(dist.metadata['keywords'], ['one', 'two'])
 
     def test_get_command_packages(self):
         dist = Distribution()
-        self.assertEquals(dist.command_packages, None)
+        self.assertEqual(dist.command_packages, None)
         cmds = dist.get_command_packages()
-        self.assertEquals(cmds, ['distutils2.command'])
-        self.assertEquals(dist.command_packages,
+        self.assertEqual(cmds, ['distutils2.command'])
+        self.assertEqual(dist.command_packages,
                           ['distutils2.command'])
 
         dist.command_packages = 'one,two'
         cmds = dist.get_command_packages()
-        self.assertEquals(cmds, ['distutils2.command', 'one', 'two'])
+        self.assertEqual(cmds, ['distutils2.command', 'one', 'two'])
 
 
     def test_announce(self):
@@ -238,7 +238,7 @@
             os.path.expanduser = old_expander
 
         # make sure --no-user-cfg disables the user cfg file
-        self.assertEquals(len(all_files)-1, len(files))
+        self.assertEqual(len(all_files)-1, len(files))
 
 
 class MetadataTestCase(support.TempdirManager, support.EnvironGuard,
@@ -340,8 +340,10 @@
         temp_dir = self.mkdtemp()
         user_filename = os.path.join(temp_dir, user_filename)
         f = open(user_filename, 'w')
-        f.write('.')
-        f.close()
+        try:
+            f.write('.')
+        finally:
+            f.close()
 
         try:
             dist = Distribution()
@@ -365,8 +367,8 @@
     def test_fix_help_options(self):
         help_tuples = [('a', 'b', 'c', 'd'), (1, 2, 3, 4)]
         fancy_options = fix_help_options(help_tuples)
-        self.assertEquals(fancy_options[0], ('a', 'b', 'c'))
-        self.assertEquals(fancy_options[1], (1, 2, 3))
+        self.assertEqual(fancy_options[0], ('a', 'b', 'c'))
+        self.assertEqual(fancy_options[1], (1, 2, 3))
 
     def test_show_help(self):
         # smoke test, just makes sure some help is displayed
@@ -412,14 +414,14 @@
         PKG_INFO.seek(0)
 
         metadata.read_file(PKG_INFO)
-        self.assertEquals(metadata['name'], "package")
-        self.assertEquals(metadata['version'], "1.0")
-        self.assertEquals(metadata['summary'], "xxx")
-        self.assertEquals(metadata['download_url'], 'http://example.com')
-        self.assertEquals(metadata['keywords'], ['one', 'two'])
-        self.assertEquals(metadata['platform'], [])
-        self.assertEquals(metadata['obsoletes'], [])
-        self.assertEquals(metadata['requires-dist'], ['foo'])
+        self.assertEqual(metadata['name'], "package")
+        self.assertEqual(metadata['version'], "1.0")
+        self.assertEqual(metadata['summary'], "xxx")
+        self.assertEqual(metadata['download_url'], 'http://example.com')
+        self.assertEqual(metadata['keywords'], ['one', 'two'])
+        self.assertEqual(metadata['platform'], [])
+        self.assertEqual(metadata['obsoletes'], [])
+        self.assertEqual(metadata['requires-dist'], ['foo'])
 
 def test_suite():
     suite = unittest.TestSuite()
diff --git a/src/distutils2/tests/test_install.py b/src/distutils2/tests/test_install.py
--- a/src/distutils2/tests/test_install.py
+++ b/src/distutils2/tests/test_install.py
@@ -139,23 +139,23 @@
 
         # two elements
         cmd.handle_extra_path()
-        self.assertEquals(cmd.extra_path, ['path', 'dirs'])
-        self.assertEquals(cmd.extra_dirs, 'dirs')
-        self.assertEquals(cmd.path_file, 'path')
+        self.assertEqual(cmd.extra_path, ['path', 'dirs'])
+        self.assertEqual(cmd.extra_dirs, 'dirs')
+        self.assertEqual(cmd.path_file, 'path')
 
         # one element
         cmd.extra_path = ['path']
         cmd.handle_extra_path()
-        self.assertEquals(cmd.extra_path, ['path'])
-        self.assertEquals(cmd.extra_dirs, 'path')
-        self.assertEquals(cmd.path_file, 'path')
+        self.assertEqual(cmd.extra_path, ['path'])
+        self.assertEqual(cmd.extra_dirs, 'path')
+        self.assertEqual(cmd.path_file, 'path')
 
         # none
         dist.extra_path = cmd.extra_path = None
         cmd.handle_extra_path()
-        self.assertEquals(cmd.extra_path, None)
-        self.assertEquals(cmd.extra_dirs, '')
-        self.assertEquals(cmd.path_file, None)
+        self.assertEqual(cmd.extra_path, None)
+        self.assertEqual(cmd.extra_dirs, '')
+        self.assertEqual(cmd.path_file, None)
 
         # three elements (no way !)
         cmd.extra_path = 'path,dirs,again'
@@ -199,7 +199,7 @@
         # line (the egg info file)
         f = open(cmd.record)
         try:
-            self.assertEquals(len(f.readlines()), 1)
+            self.assertEqual(len(f.readlines()), 1)
         finally:
             f.close()
 
diff --git a/src/distutils2/tests/test_install_data.py b/src/distutils2/tests/test_install_data.py
--- a/src/distutils2/tests/test_install_data.py
+++ b/src/distutils2/tests/test_install_data.py
@@ -27,14 +27,14 @@
         self.write_file(two, 'xxx')
 
         cmd.data_files = [one, (inst2, [two])]
-        self.assertEquals(cmd.get_inputs(), [one, (inst2, [two])])
+        self.assertEqual(cmd.get_inputs(), [one, (inst2, [two])])
 
         # let's run the command
         cmd.ensure_finalized()
         cmd.run()
 
         # let's check the result
-        self.assertEquals(len(cmd.get_outputs()), 2)
+        self.assertEqual(len(cmd.get_outputs()), 2)
         rtwo = os.path.split(two)[-1]
         self.assertTrue(os.path.exists(os.path.join(inst2, rtwo)))
         rone = os.path.split(one)[-1]
@@ -47,7 +47,7 @@
         cmd.run()
 
         # let's check the result
-        self.assertEquals(len(cmd.get_outputs()), 2)
+        self.assertEqual(len(cmd.get_outputs()), 2)
         self.assertTrue(os.path.exists(os.path.join(inst2, rtwo)))
         self.assertTrue(os.path.exists(os.path.join(inst, rone)))
         cmd.outfiles = []
@@ -65,7 +65,7 @@
         cmd.run()
 
         # let's check the result
-        self.assertEquals(len(cmd.get_outputs()), 4)
+        self.assertEqual(len(cmd.get_outputs()), 4)
         self.assertTrue(os.path.exists(os.path.join(inst2, rtwo)))
         self.assertTrue(os.path.exists(os.path.join(inst, rone)))
 
diff --git a/src/distutils2/tests/test_install_headers.py b/src/distutils2/tests/test_install_headers.py
--- a/src/distutils2/tests/test_install_headers.py
+++ b/src/distutils2/tests/test_install_headers.py
@@ -23,7 +23,7 @@
 
         pkg_dir, dist = self.create_dist(headers=headers)
         cmd = install_headers(dist)
-        self.assertEquals(cmd.get_inputs(), headers)
+        self.assertEqual(cmd.get_inputs(), headers)
 
         # let's run the command
         cmd.install_dir = os.path.join(pkg_dir, 'inst')
@@ -31,7 +31,7 @@
         cmd.run()
 
         # let's check the results
-        self.assertEquals(len(cmd.get_outputs()), 2)
+        self.assertEqual(len(cmd.get_outputs()), 2)
 
 def test_suite():
     return unittest.makeSuite(InstallHeadersTestCase)
diff --git a/src/distutils2/tests/test_install_lib.py b/src/distutils2/tests/test_install_lib.py
--- a/src/distutils2/tests/test_install_lib.py
+++ b/src/distutils2/tests/test_install_lib.py
@@ -25,8 +25,8 @@
         cmd = install_lib(dist)
 
         cmd.finalize_options()
-        self.assertEquals(cmd.compile, 1)
-        self.assertEquals(cmd.optimize, 0)
+        self.assertEqual(cmd.compile, 1)
+        self.assertEqual(cmd.optimize, 0)
 
         # optimize must be 0, 1, or 2
         cmd.optimize = 'foo'
@@ -36,7 +36,7 @@
 
         cmd.optimize = '2'
         cmd.finalize_options()
-        self.assertEquals(cmd.optimize, 2)
+        self.assertEqual(cmd.optimize, 2)
 
     @unittest.skipIf(no_bytecode, 'byte-compile not supported')
     def test_byte_compile(self):
@@ -82,7 +82,7 @@
         cmd.distribution.script_name = 'setup.py'
 
         # get_input should return 2 elements
-        self.assertEquals(len(cmd.get_inputs()), 2)
+        self.assertEqual(len(cmd.get_inputs()), 2)
 
     @unittest.skipUnless(bytecode_support, 'sys.dont_write_bytecode not supported')
     def test_dont_write_bytecode(self):
diff --git a/src/distutils2/tests/test_install_scripts.py b/src/distutils2/tests/test_install_scripts.py
--- a/src/distutils2/tests/test_install_scripts.py
+++ b/src/distutils2/tests/test_install_scripts.py
@@ -42,8 +42,10 @@
         def write_script(name, text):
             expected.append(name)
             f = open(os.path.join(source, name), "w")
-            f.write(text)
-            f.close()
+            try:
+                f.write(text)
+            finally:
+                f.close()
 
         write_script("script1.py", ("#! /usr/bin/env python2.3\n"
                                     "# bogus script w/ Python sh-bang\n"
diff --git a/src/distutils2/tests/test_manifest.py b/src/distutils2/tests/test_manifest.py
--- a/src/distutils2/tests/test_manifest.py
+++ b/src/distutils2/tests/test_manifest.py
@@ -3,6 +3,7 @@
 import sys
 import logging
 
+from distutils2.tests import run_unittest
 from distutils2.tests import support
 from distutils2.tests.support import unittest
 from distutils2.manifest import Manifest
@@ -44,7 +45,7 @@
 
         # the manifest should have been read
         # and 3 warnings issued (we ddidn't provided the files)
-        self.assertEquals(len(warns), 3)
+        self.assertEqual(len(warns), 3)
         for warn in warns:
             self.assertIn('warning: no files found matching', warn)
 
diff --git a/src/distutils2/tests/test_metadata.py b/src/distutils2/tests/test_metadata.py
--- a/src/distutils2/tests/test_metadata.py
+++ b/src/distutils2/tests/test_metadata.py
@@ -5,9 +5,12 @@
 
 from distutils2.metadata import (DistributionMetadata, _interpret,
                                  PKG_INFO_PREFERRED_VERSION)
-from distutils2.tests.support import unittest
+from distutils2.tests import run_unittest
+from distutils2.tests.support import unittest, LoggingSilencer
+from distutils2.errors import (MetadataConflictError,
+                               MetadataUnrecognizedVersionError)
 
-class DistributionMetadataTestCase(unittest.TestCase):
+class DistributionMetadataTestCase(LoggingSilencer, unittest.TestCase):
 
 
     def test_interpret(self):
@@ -15,15 +18,15 @@
         version = sys.version.split()[0]
         os_name = os.name
 
-        assert _interpret("sys.platform == '%s'" % platform)
-        assert _interpret("sys.platform == '%s' or python_version == '2.4'" \
-                % platform)
-        assert _interpret("sys.platform == '%s' and "
-                          "python_full_version == '%s'"\
-                % (platform, version))
-        assert _interpret("'%s' == sys.platform" % platform)
+        self.assertTrue(_interpret("sys.platform == '%s'" % platform))
+        self.assertTrue(_interpret(
+            "sys.platform == '%s' or python_version == '2.4'" % platform))
+        self.assertTrue(_interpret(
+            "sys.platform == '%s' and python_full_version == '%s'" %
+            (platform, version)))
+        self.assertTrue(_interpret("'%s' == sys.platform" % platform))
 
-        assert _interpret('os.name == "%s"' % os_name)
+        self.assertTrue(_interpret('os.name == "%s"' % os_name))
 
         # stuff that need to raise a syntax error
         ops = ('os.name == os.name', 'os.name == 2', "'2' == '2'",
@@ -35,24 +38,25 @@
         OP = 'os.name == "%s"' % os_name
         AND = ' and '
         OR = ' or '
-        assert _interpret(OP+AND+OP)
-        assert _interpret(OP+AND+OP+AND+OP)
-        assert _interpret(OP+OR+OP)
-        assert _interpret(OP+OR+OP+OR+OP)
+        self.assertTrue(_interpret(OP + AND + OP))
+        self.assertTrue(_interpret(OP + AND + OP + AND + OP))
+        self.assertTrue(_interpret(OP + OR + OP))
+        self.assertTrue(_interpret(OP + OR + OP + OR + OP))
 
         # other operators
-        assert _interpret("os.name != 'buuuu'")
-        assert _interpret("python_version > '1.0'")
-        assert _interpret("python_version < '5.0'")
-        assert _interpret("python_version <= '5.0'")
-        assert _interpret("python_version >= '1.0'")
-        assert _interpret("'%s' in os.name" % os_name)
-        assert _interpret("'buuuu' not in os.name")
-        assert _interpret("'buuuu' not in os.name and '%s' in os.name" \
-                            % os_name)
+        self.assertTrue(_interpret("os.name != 'buuuu'"))
+        self.assertTrue(_interpret("python_version > '1.0'"))
+        self.assertTrue(_interpret("python_version < '5.0'"))
+        self.assertTrue(_interpret("python_version <= '5.0'"))
+        self.assertTrue(_interpret("python_version >= '1.0'"))
+        self.assertTrue(_interpret("'%s' in os.name" % os_name))
+        self.assertTrue(_interpret("'buuuu' not in os.name"))
+        self.assertTrue(_interpret(
+            "'buuuu' not in os.name and '%s' in os.name" % os_name))
 
         # execution context
-        assert _interpret('python_version == "0.1"', {'python_version': '0.1'})
+        self.assertTrue(_interpret('python_version == "0.1"',
+                                   {'python_version': '0.1'}))
 
     def test_metadata_read_write(self):
 
@@ -63,25 +67,28 @@
         res.seek(0)
         res = res.read()
         f = open(PKG_INFO)
-        wanted = f.read()
+        try:
+            # XXX this is not used
+            wanted = f.read()
+        finally:
+            f.close()
         self.assertTrue('Keywords: keyring,password,crypt' in res)
-        f.close()
 
     def test_metadata_markers(self):
         # see if we can be platform-aware
         PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
         content = open(PKG_INFO).read()
         content = content % sys.platform
-        metadata = DistributionMetadata(platform_dependant=True)
+        metadata = DistributionMetadata(platform_dependent=True)
         metadata.read_file(StringIO(content))
-        self.assertEquals(metadata['Requires-Dist'], ['bar'])
+        self.assertEqual(metadata['Requires-Dist'], ['bar'])
 
         # test with context
         context = {'sys.platform': 'okook'}
-        metadata = DistributionMetadata(platform_dependant=True,
+        metadata = DistributionMetadata(platform_dependent=True,
                                         execution_context=context)
         metadata.read_file(StringIO(content))
-        self.assertEquals(metadata['Requires-Dist'], ['foo'])
+        self.assertEqual(metadata['Requires-Dist'], ['foo'])
 
     def test_description(self):
         PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
@@ -93,14 +100,14 @@
         # see if we can read the description now
         DESC = os.path.join(os.path.dirname(__file__), 'LONG_DESC.txt')
         wanted = open(DESC).read()
-        self.assertEquals(wanted, metadata['Description'])
+        self.assertEqual(wanted, metadata['Description'])
 
         # save the file somewhere and make sure we can read it back
         out = StringIO()
         metadata.write_file(out)
         out.seek(0)
         metadata.read_file(out)
-        self.assertEquals(wanted, metadata['Description'])
+        self.assertEqual(wanted, metadata['Description'])
 
     def test_mapper_apis(self):
         PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
@@ -115,25 +122,32 @@
     def test_versions(self):
         metadata = DistributionMetadata()
         metadata['Obsoletes'] = 'ok'
-        self.assertEquals(metadata['Metadata-Version'], '1.1')
+        self.assertEqual(metadata['Metadata-Version'], '1.1')
 
         del metadata['Obsoletes']
         metadata['Obsoletes-Dist'] = 'ok'
-        self.assertEquals(metadata['Metadata-Version'], '1.2')
+        self.assertEqual(metadata['Metadata-Version'], '1.2')
 
+        self.assertRaises(MetadataConflictError, metadata.set,
+                          'Obsoletes', 'ok')
+
+        del metadata['Obsoletes']
         del metadata['Obsoletes-Dist']
         metadata['Version'] = '1'
-        self.assertEquals(metadata['Metadata-Version'], '1.0')
+        self.assertEqual(metadata['Metadata-Version'], '1.0')
 
         PKG_INFO = os.path.join(os.path.dirname(__file__),
                                 'SETUPTOOLS-PKG-INFO')
         metadata.read_file(StringIO(open(PKG_INFO).read()))
-        self.assertEquals(metadata['Metadata-Version'], '1.0')
+        self.assertEqual(metadata['Metadata-Version'], '1.0')
 
         PKG_INFO = os.path.join(os.path.dirname(__file__),
                                 'SETUPTOOLS-PKG-INFO2')
         metadata.read_file(StringIO(open(PKG_INFO).read()))
-        self.assertEquals(metadata['Metadata-Version'], '1.1')
+        self.assertEqual(metadata['Metadata-Version'], '1.1')
+
+        metadata.version = '1.618'
+        self.assertRaises(MetadataUnrecognizedVersionError, metadata.keys)
 
     def test_warnings(self):
         metadata = DistributionMetadata()
@@ -161,7 +175,7 @@
 
         # we should have a certain amount of warnings
         num_wanted = len(values)
-        self.assertEquals(num_wanted, res)
+        self.assertEqual(num_wanted, res)
 
     def test_multiple_predicates(self):
         metadata = DistributionMetadata()
@@ -184,29 +198,29 @@
             res = m.warns
             del m.warns
 
-        self.assertEquals(res, 0)
+        self.assertEqual(res, 0)
 
     def test_project_url(self):
         metadata = DistributionMetadata()
         metadata['Project-URL'] = [('one', 'http://ok')]
-        self.assertEquals(metadata['Project-URL'],
+        self.assertEqual(metadata['Project-URL'],
                           [('one', 'http://ok')])
-        self.assertEquals(metadata.version, '1.2')
+        self.assertEqual(metadata.version, '1.2')
 
     def test_check(self):
         metadata = DistributionMetadata()
         metadata['Version'] = 'rr'
         metadata['Requires-dist'] = ['Foo (a)']
         missing, warnings = metadata.check()
-        self.assertEquals(missing, ['Name', 'Home-page'])
-        self.assertEquals(len(warnings), 2)
+        self.assertEqual(missing, ['Name', 'Home-page'])
+        self.assertEqual(len(warnings), 2)
 
     def test_best_choice(self):
         metadata = DistributionMetadata()
         metadata['Version'] = '1.0'
-        self.assertEquals(metadata.version, PKG_INFO_PREFERRED_VERSION)
+        self.assertEqual(metadata.version, PKG_INFO_PREFERRED_VERSION)
         metadata['Classifier'] = ['ok']
-        self.assertEquals(metadata.version, '1.2')
+        self.assertEqual(metadata.version, '1.2')
 
     def test_project_urls(self):
         # project-url is a bit specific, make sure we write it
@@ -214,7 +228,7 @@
         metadata = DistributionMetadata()
         metadata['Version'] = '1.0'
         metadata['Project-Url'] = [('one', 'http://ok')]
-        self.assertEquals(metadata['Project-Url'], [('one', 'http://ok')])
+        self.assertEqual(metadata['Project-Url'], [('one', 'http://ok')])
         file_ = StringIO()
         metadata.write_file(file_)
         file_.seek(0)
@@ -224,7 +238,7 @@
         file_.seek(0)
         metadata = DistributionMetadata()
         metadata.read_file(file_)
-        self.assertEquals(metadata['Project-Url'], [('one', 'http://ok')])
+        self.assertEqual(metadata['Project-Url'], [('one', 'http://ok')])
 
 
 def test_suite():
diff --git a/src/distutils2/tests/test_msvc9compiler.py b/src/distutils2/tests/test_msvc9compiler.py
--- a/src/distutils2/tests/test_msvc9compiler.py
+++ b/src/distutils2/tests/test_msvc9compiler.py
@@ -105,7 +105,7 @@
         import _winreg
         HKCU = _winreg.HKEY_CURRENT_USER
         keys = Reg.read_keys(HKCU, 'xxxx')
-        self.assertEquals(keys, None)
+        self.assertEqual(keys, None)
 
         keys = Reg.read_keys(HKCU, r'Control Panel')
         self.assertTrue('Desktop' in keys)
@@ -116,20 +116,24 @@
         tempdir = self.mkdtemp()
         manifest = os.path.join(tempdir, 'manifest')
         f = open(manifest, 'w')
-        f.write(_MANIFEST)
-        f.close()
+        try:
+            f.write(_MANIFEST)
+        finally:
+            f.close()
 
         compiler = MSVCCompiler()
         compiler._remove_visual_c_ref(manifest)
 
         # see what we got
         f = open(manifest)
-        # removing trailing spaces
-        content = '\n'.join([line.rstrip() for line in f.readlines()])
-        f.close()
+        try:
+            # removing trailing spaces
+            content = '\n'.join([line.rstrip() for line in f.readlines()])
+        finally:
+            f.close()
 
         # makes sure the manifest was properly cleaned
-        self.assertEquals(content, _CLEANED_MANIFEST)
+        self.assertEqual(content, _CLEANED_MANIFEST)
 
 
 def test_suite():
diff --git a/src/distutils2/tests/test_pypi_dist.py b/src/distutils2/tests/test_pypi_dist.py
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/test_pypi_dist.py
@@ -0,0 +1,247 @@
+"""Tests for the distutils2.pypi.dist module."""
+
+import os
+import shutil
+import tempfile
+
+from distutils2.tests.pypi_server import use_pypi_server
+from distutils2.tests import run_unittest
+from distutils2.tests.support import unittest, TempdirManager
+from distutils2.version import VersionPredicate
+from distutils2.pypi.errors import HashDoesNotMatch, UnsupportedHashName
+from distutils2.pypi.dist import (PyPIDistribution as Dist,
+                                  PyPIDistributions as Dists,
+                                  split_archive_name)
+
+
+class TestPyPIDistribution(TempdirManager,
+                           unittest.TestCase):
+    """Tests the pypi.dist.PyPIDistribution class"""
+
+    def test_instanciation(self):
+        # Test the Distribution class provides us the good attributes when
+        # given on construction
+        dist = Dist("FooBar", "1.1")
+        self.assertEqual("FooBar", dist.name)
+        self.assertEqual("1.1", "%s" % dist.version)
+
+    def test_create_from_url(self):
+        # Test that the Distribution object can be built from a single URL
+        url_list = {
+            'FooBar-1.1.0.tar.gz': {
+                'name': 'foobar',  # lowercase the name
+                'version': '1.1',
+            },
+            'Foo-Bar-1.1.0.zip': {
+                'name': 'foo-bar',  # keep the dash
+                'version': '1.1',
+            },
+            'foobar-1.1b2.tar.gz#md5=123123123123123': {
+                'name': 'foobar',
+                'version': '1.1b2',
+                'url': {
+                    'url': 'http://test.tld/foobar-1.1b2.tar.gz',  # no hash
+                    'hashval': '123123123123123',
+                    'hashname': 'md5',
+                }
+            },
+            'foobar-1.1-rc2.tar.gz': {  # use suggested name
+                'name': 'foobar',
+                'version': '1.1c2',
+                'url': {
+                    'url': 'http://test.tld/foobar-1.1-rc2.tar.gz',
+                }
+            }
+        }
+
+        for url, attributes in url_list.items():
+            dist = Dist.from_url("http://test.tld/" + url)
+            for attribute, value in attributes.items():
+                if isinstance(value, dict):
+                    mylist = getattr(dist, attribute)
+                    for val in value.keys():
+                        self.assertEqual(value[val], mylist[val])
+                else:
+                    if attribute == "version":
+                        self.assertEqual("%s" % getattr(dist, "version"), value)
+                    else:
+                        self.assertEqual(getattr(dist, attribute), value)
+
+    def test_get_url(self):
+        # Test that the url property works well
+
+        d = Dist("foobar", "1.1", url="test_url")
+        self.assertDictEqual(d.url, {
+            "url": "test_url",
+            "is_external": True,
+            "hashname": None,
+            "hashval": None,
+        })
+
+        # add a new url
+        d.add_url(url="internal_url", is_external=False)
+        self.assertEqual(d._url, None)
+        self.assertDictEqual(d.url, {
+            "url": "internal_url",
+            "is_external": False,
+            "hashname": None,
+            "hashval": None,
+        })
+        self.assertEqual(2, len(d._urls))
+
+    def test_comparaison(self):
+        # Test that we can compare PyPIDistributions
+        foo1 = Dist("foo", "1.0")
+        foo2 = Dist("foo", "2.0")
+        bar = Dist("bar", "2.0")
+        # assert we use the version to compare
+        self.assertTrue(foo1 < foo2)
+        self.assertFalse(foo1 > foo2)
+        self.assertFalse(foo1 == foo2)
+
+        # assert we can't compare dists with different names
+        self.assertRaises(TypeError, foo1.__eq__, bar)
+
+    def test_split_archive_name(self):
+        # Test we can split the archive names
+        names = {
+            'foo-bar-baz-1.0-rc2': ('foo-bar-baz', '1.0c2'),
+            'foo-bar-baz-1.0': ('foo-bar-baz', '1.0'),
+            'foobarbaz-1.0': ('foobarbaz', '1.0'),
+        }
+        for name, results in names.items():
+            self.assertEqual(results, split_archive_name(name))
+
+    @use_pypi_server("downloads_with_md5")
+    def test_download(self, server):
+        # Download is possible, and the md5 is checked if given
+
+        add_to_tmpdirs = lambda x: self.tempdirs.append(os.path.dirname(x))
+
+        url = "%s/simple/foobar/foobar-0.1.tar.gz" % server.full_address
+        # check md5 if given
+        dist = Dist("FooBar", "0.1", url=url,
+            url_hashname="md5", url_hashval="d41d8cd98f00b204e9800998ecf8427e")
+        add_to_tmpdirs(dist.download())
+
+        # a wrong md5 fails
+        dist2 = Dist("FooBar", "0.1", url=url,
+            url_hashname="md5", url_hashval="wrongmd5")
+
+        self.assertRaises(HashDoesNotMatch, dist2.download)
+        add_to_tmpdirs(dist2.downloaded_location)
+
+        # we can omit the md5 hash
+        dist3 = Dist("FooBar", "0.1", url=url)
+        add_to_tmpdirs(dist3.download())
+
+        # and specify a temporary location
+        # for an already downloaded dist
+        path1 = self.mkdtemp()
+        dist3.download(path=path1)
+        # and for a new one
+        path2_base = self.mkdtemp()
+        dist4 = Dist("FooBar", "0.1", url=url)
+        path2 = dist4.download(path=path2_base)
+        self.assertTrue(path2_base in path2)
+
+    def test_hashname(self):
+        # Invalid hashnames raises an exception on assignation
+        Dist("FooBar", "0.1", url_hashname="md5", url_hashval="value")
+
+        self.assertRaises(UnsupportedHashName, Dist, "FooBar", "0.1",
+                          url_hashname="invalid_hashname", url_hashval="value")
+
+
+class TestPyPIDistributions(unittest.TestCase):
+
+    def test_filter(self):
+        # Test we filter the distributions the right way, using version
+        # predicate match method
+        dists = Dists((
+            Dist("FooBar", "1.1"),
+            Dist("FooBar", "1.1.1"),
+            Dist("FooBar", "1.2"),
+            Dist("FooBar", "1.2.1"),
+        ))
+        filtered = dists.filter(VersionPredicate("FooBar (<1.2)"))
+        self.assertNotIn(dists[2], filtered)
+        self.assertNotIn(dists[3], filtered)
+        self.assertIn(dists[0], filtered)
+        self.assertIn(dists[1], filtered)
+
+    def test_append(self):
+        # When adding a new item to the list, the behavior is to test if
+        # a distribution with the same name and version number already exists,
+        # and if so, to add url informations to the existing PyPIDistribution
+        # object.
+        # If no object matches, just add "normally" the object to the list.
+
+        dists = Dists([
+            Dist("FooBar", "1.1", url="external_url", type="source"),
+        ])
+        self.assertEqual(1, len(dists))
+        dists.append(Dist("FooBar", "1.1", url="internal_url",
+                          url_is_external=False, type="source"))
+        self.assertEqual(1, len(dists))
+        self.assertEqual(2, len(dists[0]._urls))
+
+        dists.append(Dist("Foobar", "1.1.1", type="source"))
+        self.assertEqual(2, len(dists))
+
+        # when adding a distribution whith a different type, a new distribution
+        # has to be added.
+        dists.append(Dist("Foobar", "1.1.1", type="binary"))
+        self.assertEqual(3, len(dists))
+
+    def test_prefer_final(self):
+        # Can order the distributions using prefer_final
+
+        fb10 = Dist("FooBar", "1.0")  # final distribution
+        fb11a = Dist("FooBar", "1.1a1")  # alpha
+        fb12a = Dist("FooBar", "1.2a1")  # alpha
+        fb12b = Dist("FooBar", "1.2b1")  # beta
+        dists = Dists([fb10, fb11a, fb12a, fb12b])
+
+        dists.sort_distributions(prefer_final=True)
+        self.assertEqual(fb10, dists[0])
+
+        dists.sort_distributions(prefer_final=False)
+        self.assertEqual(fb12b, dists[0])
+
+    def test_prefer_source(self):
+        # Ordering support prefer_source
+        fb_source = Dist("FooBar", "1.0", type="source")
+        fb_binary = Dist("FooBar", "1.0", type="binary")
+        fb2_binary = Dist("FooBar", "2.0", type="binary")
+        dists = Dists([fb_binary, fb_source])
+
+        dists.sort_distributions(prefer_source=True)
+        self.assertEqual(fb_source, dists[0])
+
+        dists.sort_distributions(prefer_source=False)
+        self.assertEqual(fb_binary, dists[0])
+
+        dists.append(fb2_binary)
+        dists.sort_distributions(prefer_source=True)
+        self.assertEqual(fb2_binary, dists[0])
+
+    def test_get_same_name_and_version(self):
+        # PyPIDistributions can return a list of "duplicates"
+        fb_source = Dist("FooBar", "1.0", type="source")
+        fb_binary = Dist("FooBar", "1.0", type="binary")
+        fb2_binary = Dist("FooBar", "2.0", type="binary")
+        dists = Dists([fb_binary, fb_source, fb2_binary])
+        duplicates = dists.get_same_name_and_version()
+        self.assertTrue(1, len(duplicates))
+        self.assertIn(fb_source, duplicates[0])
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(TestPyPIDistribution))
+    suite.addTest(unittest.makeSuite(TestPyPIDistributions))
+    return suite
+
+if __name__ == '__main__':
+    run_unittest(test_suite())
diff --git a/src/distutils2/tests/test_pypi_server.py b/src/distutils2/tests/test_pypi_server.py
--- a/src/distutils2/tests/test_pypi_server.py
+++ b/src/distutils2/tests/test_pypi_server.py
@@ -10,7 +10,7 @@
 class PyPIServerTest(unittest.TestCase):
 
     def test_records_requests(self):
-        """We expect that PyPIServer can log our requests"""
+        # We expect that PyPIServer can log our requests
         server = PyPIServer()
         server.start()
         self.assertEqual(len(server.requests), 0)
@@ -23,14 +23,13 @@
         self.assertEqual(len(server.requests), 1)
         handler, request_data = server.requests[-1]
         self.assertIn("Rock Around The Bunker", request_data)
-        self.assertTrue(handler.headers.dict.has_key("x-test-header"))
-        self.assertEqual(handler.headers.dict["x-test-header"], 
-            "Mister Iceberg")
+        self.assertIn("x-test-header", handler.headers.dict)
+        self.assertEqual(handler.headers.dict["x-test-header"],
+                         "Mister Iceberg")
         server.stop()
 
     def test_serve_static_content(self):
-        """PYPI Mocked server can serve static content from disk.
-        """
+        # PYPI Mocked server can serve static content from disk.
 
         def uses_local_files_for(server, url_path):
             """Test that files are served statically (eg. the output from the
diff --git a/src/distutils2/tests/test_pypi_simple.py b/src/distutils2/tests/test_pypi_simple.py
--- a/src/distutils2/tests/test_pypi_simple.py
+++ b/src/distutils2/tests/test_pypi_simple.py
@@ -5,20 +5,34 @@
 import os
 import shutil
 import tempfile
-import unittest2
 import urllib2
 
-from distutils2.tests.pypi_server import use_pypi_server
 from distutils2.pypi import simple
+from distutils2.tests import support, run_unittest
+from distutils2.tests.support import unittest
+from distutils2.tests.pypi_server import (use_pypi_server, PyPIServer,
+                                          PYPI_DEFAULT_STATIC_PATH)
 
 
-class PyPISimpleTestCase(unittest2.TestCase):
+class PyPISimpleTestCase(support.TempdirManager,
+                         unittest.TestCase):
+
+    def _get_simple_index(self, server, base_url="/simple/", hosts=None,
+                          *args, **kwargs):
+        """Build and return a SimpleSimpleIndex instance, with the test server
+        urls
+        """
+        if hosts is None:
+            hosts = (server.full_address.strip("http://"),)
+        kwargs['hosts'] = hosts
+        return simple.SimpleIndex(server.full_address + base_url, *args,
+            **kwargs)
 
     def test_bad_urls(self):
-        index = simple.PackageIndex()
+        index = simple.SimpleIndex()
         url = 'http://127.0.0.1:0/nonesuch/test_simple'
         try:
-            v = index.open_url(url)
+            v = index._open_url(url)
         except Exception, v:
             self.assertTrue(url in str(v))
         else:
@@ -27,12 +41,10 @@
         # issue 16
         # easy_install inquant.contentmirror.plone breaks because of a typo
         # in its home URL
-        index = simple.PackageIndex(
-            hosts=('www.example.com',))
-
+        index = simple.SimpleIndex(hosts=('www.example.com',))
         url = 'url:%20https://svn.plone.org/svn/collective/inquant.contentmirror.plone/trunk'
         try:
-            v = index.open_url(url)
+            v = index._open_url(url)
         except Exception, v:
             self.assertTrue(url in str(v))
         else:
@@ -47,7 +59,7 @@
         url = 'http://example.com'
         try:
             try:
-                v = index.open_url(url)
+                v = index._open_url(url)
             except Exception, v:
                 self.assertTrue('line' in str(v))
             else:
@@ -58,7 +70,7 @@
         # issue 20
         url = 'http://http://svn.pythonpaste.org/Paste/wphp/trunk'
         try:
-            index.open_url(url)
+            index._open_url(url)
         except Exception, v:
             self.assertTrue('nonnumeric port' in str(v))
 
@@ -68,103 +80,198 @@
             url = 'http://example.com'
             page = ('<a href="http://www.famfamfam.com]('
                     'http://www.famfamfam.com/">')
-            index.process_index(url, page)
+            index._process_url(url, page)
 
-    def test_url_ok(self):
-        index = simple.PackageIndex(
-            hosts=('www.example.com',))
-        url = 'file:///tmp/test_simple'
-        self.assertTrue(index.url_ok(url, True))
-    
     @use_pypi_server("test_found_links")
     def test_found_links(self, server):
-        """Browse the index, asking for a specified distribution version
-        """
+        # Browse the index, asking for a specified distribution version
         # The PyPI index contains links for version 1.0, 1.1, 2.0 and 2.0.1
-        # We query only for the version 1.1, so all distributions must me
-        # filled in the package_index (as the url has been scanned), but
-        # obtain must only return the one we want.
-        pi = simple.PackageIndex(server.full_address + "/simple/")
-        last_distribution = pi.obtain(simple.Requirement.parse("foobar"))
+        index = self._get_simple_index(server)
+        last_distribution = index.get("foobar")
 
         # we have scanned the index page
-        self.assertTrue(pi.package_pages.has_key('foobar'))
         self.assertIn(server.full_address + "/simple/foobar/",
-            pi.package_pages['foobar'])
-        
+            index._processed_urls)
+
         # we have found 4 distributions in this page
-        self.assertEqual(len(pi["foobar"]), 4)
+        self.assertEqual(len(index._distributions["foobar"]), 4)
 
         # and returned the most recent one
-        self.assertEqual(last_distribution.version, '2.0.1')
+        self.assertEqual("%s" % last_distribution.version, '2.0.1')
 
-    @use_pypi_server("with_external_pages")
-    def test_process_external_pages(self, server):
-        """If the index provides links external to the pypi index, 
-        they need to be processed, in order to discover new distributions.
-        """
+    def test_is_browsable(self):
+        index = simple.SimpleIndex(follow_externals=False)
+        self.assertTrue(index._is_browsable(index.index_url + "test"))
 
-    @use_pypi_server()
-    def test_scan_all(self, server):
-        """Test that we can process the whole index and discover all related
-        links.
+        # Now, when following externals, we can have a list of hosts to trust.
+        # and don't follow other external links than the one described here.
+        index = simple.SimpleIndex(hosts=["pypi.python.org", "test.org"],
+                                   follow_externals=True)
+        good_urls = (
+            "http://pypi.python.org/foo/bar",
+            "http://pypi.python.org/simple/foobar",
+            "http://test.org",
+            "http://test.org/",
+            "http://test.org/simple/",
+        )
+        bad_urls = (
+            "http://python.org",
+            "http://test.tld",
+        )
 
-        """
-        # FIXME remove?
+        for url in good_urls:
+            self.assertTrue(index._is_browsable(url))
 
-    @use_pypi_server("with_external_pages")
-    def test_disable_external_pages(self, server):
-        """Test that when we tell the simple client to not retreive external
-        urls, it does well.""" 
-    
-    @use_pypi_server()
-    def test_process_index(self, server):
-        """Test that we can process a simple given string and find related links
-        to distributions.
-        """
-    
-    @use_pypi_server()
-    def test_process_url(self, server):
-        """Test that we can process an alternative url (not pypi related) to
-        find links in it.
-        """
-    
-    @use_pypi_server(static_filesystem_paths=["test_link_priority"],
+        for url in bad_urls:
+            self.assertFalse(index._is_browsable(url))
+
+        # allow all hosts
+        index = simple.SimpleIndex(follow_externals=True, hosts=("*",))
+        self.assertTrue(index._is_browsable("http://an-external.link/path"))
+        self.assertTrue(index._is_browsable("pypi.test.tld/a/path"))
+
+        # specify a list of hosts we want to allow
+        index = simple.SimpleIndex(follow_externals=True,
+                                   hosts=("*.test.tld",))
+        self.assertFalse(index._is_browsable("http://an-external.link/path"))
+        self.assertTrue(index._is_browsable("http://pypi.test.tld/a/path"))
+
+    @use_pypi_server("with_externals")
+    def test_restrict_hosts(self, server):
+        # Include external pages
+        # Try to request the package index, wich contains links to "externals"
+        # resources. They have to  be scanned too.
+        index = self._get_simple_index(server, follow_externals=True)
+        index.get("foobar")
+        self.assertIn(server.full_address + "/external/external.html",
+            index._processed_urls)
+
+    @use_pypi_server("with_real_externals")
+    def test_restrict_hosts(self, server):
+        # Only use a list of allowed hosts is possible
+        # Test that telling the simple pyPI client to not retrieve external
+        # works
+        index = self._get_simple_index(server, follow_externals=False)
+        index.get("foobar")
+        self.assertNotIn(server.full_address + "/external/external.html",
+            index._processed_urls)
+
+    @use_pypi_server(static_filesystem_paths=["with_externals"],
         static_uri_paths=["simple", "external"])
     def test_links_priority(self, server):
-        """
-        Download links from the pypi simple index should be used before
-        external download links.
-        http://bitbucket.org/tarek/distribute/issue/163/md5-validation-error
+        # Download links from the pypi simple index should be used before
+        # external download links.
+        # http://bitbucket.org/tarek/distribute/issue/163/md5-validation-error
+        #
+        # Usecase :
+        # - someone uploads a package on pypi, a md5 is generated
+        # - someone manually coindexes this link (with the md5 in the url) onto
+        #   an external page accessible from the package page.
+        # - someone reuploads the package (with a different md5)
+        # - while easy_installing, an MD5 error occurs because the external link
+        #   is used
+        # -> The index should use the link from pypi, not the external one.
 
-        Usecase :
-        - someone uploads a package on pypi, a md5 is generated
-        - someone manually copies this link (with the md5 in the url) onto an
-          external page accessible from the package page.
-        - someone reuploads the package (with a different md5)
-        - while easy_installing, an MD5 error occurs because the external link
-          is used
-        -> Distribute should use the link from pypi, not the external one.
-        """
         # start an index server
         index_url = server.full_address + '/simple/'
 
         # scan a test index
-        pi = simple.PackageIndex(index_url)
-        requirement = simple.Requirement.parse('foobar')
-        pi.find_packages(requirement)
+        index = simple.SimpleIndex(index_url, follow_externals=True)
+        dists = index.find("foobar")
         server.stop()
 
-        # the distribution has been found
-        self.assertTrue('foobar' in pi)
         # we have only one link, because links are compared without md5
-        self.assertEqual(len(pi['foobar']), 1)
+        self.assertEqual(len(dists), 1)
         # the link should be from the index
-        self.assertTrue('correct_md5' in pi['foobar'][0].location)
+        self.assertEqual('12345678901234567', dists[0].url['hashval'])
+        self.assertEqual('md5', dists[0].url['hashname'])
 
+    @use_pypi_server(static_filesystem_paths=["with_norel_links"],
+        static_uri_paths=["simple", "external"])
+    def test_not_scan_all_links(self, server):
+        # Do not follow all index page links.
+        # The links not tagged with rel="download" and rel="homepage" have
+        # to not be processed by the package index, while processing "pages".
+
+        # process the pages
+        index = self._get_simple_index(server, follow_externals=True)
+        index.find("foobar")
+        # now it should have processed only pages with links rel="download"
+        # and rel="homepage"
+        self.assertIn("%s/simple/foobar/" % server.full_address,
+            index._processed_urls)  # it's the simple index page
+        self.assertIn("%s/external/homepage.html" % server.full_address,
+            index._processed_urls)  # the external homepage is rel="homepage"
+        self.assertNotIn("%s/external/nonrel.html" % server.full_address,
+            index._processed_urls)  # this link contains no rel=*
+        self.assertNotIn("%s/unrelated-0.2.tar.gz" % server.full_address,
+            index._processed_urls)  # linked from simple index (no rel)
+        self.assertIn("%s/foobar-0.1.tar.gz" % server.full_address,
+            index._processed_urls)  # linked from simple index (rel)
+        self.assertIn("%s/foobar-2.0.tar.gz" % server.full_address,
+            index._processed_urls)  # linked from external homepage (rel)
+
+    def test_uses_mirrors(self):
+        # When the main repository seems down, try using the given mirrors"""
+        server = PyPIServer("foo_bar_baz")
+        mirror = PyPIServer("foo_bar_baz")
+        mirror.start()  # we dont start the server here
+
+        try:
+            # create the index using both servers
+            index = simple.SimpleIndex(server.full_address + "/simple/",
+                hosts=('*',), timeout=1,  # set the timeout to 1s for the tests
+                mirrors=[mirror.full_address + "/simple/",])
+
+            # this should not raise a timeout
+            self.assertEqual(4, len(index.find("foo")))
+        finally:
+            mirror.stop()
+
+    def test_simple_link_matcher(self):
+        # Test that the simple link matcher yields the right links"""
+        index = simple.SimpleIndex(follow_externals=False)
+
+        # Here, we define:
+        #   1. one link that must be followed, cause it's a download one
+        #   2. one link that must *not* be followed, cause the is_browsable
+        #      returns false for it.
+        #   3. one link that must be followed cause it's a homepage that is
+        #      browsable
+        self.assertTrue(index._is_browsable("%stest" % index.index_url))
+        self.assertFalse(index._is_browsable("http://dl-link2"))
+        content = """
+        <a href="http://dl-link1" rel="download">download_link1</a>
+        <a href="http://dl-link2" rel="homepage">homepage_link1</a>
+        <a href="%stest" rel="homepage">homepage_link2</a>
+        """ % index.index_url
+
+        # Test that the simple link matcher yield the good links.
+        generator = index._simple_link_matcher(content, index.index_url)
+        self.assertEqual(('http://dl-link1', True), generator.next())
+        self.assertEqual(('%stest' % index.index_url, False),
+                         generator.next())
+        self.assertRaises(StopIteration, generator.next)
+
+        # Follow the external links is possible
+        index.follow_externals = True
+        generator = index._simple_link_matcher(content, index.index_url)
+        self.assertEqual(('http://dl-link1', True), generator.next())
+        self.assertEqual(('http://dl-link2', False), generator.next())
+        self.assertEqual(('%stest' % index.index_url, False),
+                         generator.next())
+        self.assertRaises(StopIteration, generator.next)
+
+    def test_browse_local_files(self):
+        # Test that we can browse local files"""
+        index_path = os.sep.join(["file://" + PYPI_DEFAULT_STATIC_PATH,
+                                  "test_found_links", "simple"])
+        index = simple.SimpleIndex(index_path)
+        dists = index.find("foobar")
+        self.assertEqual(4, len(dists))
 
 def test_suite():
-    return unittest2.makeSuite(PyPISimpleTestCase)
+    return unittest.makeSuite(PyPISimpleTestCase)
 
 if __name__ == '__main__':
-    unittest2.main(defaultTest="test_suite")
+    unittest.main(defaultTest="test_suite")
diff --git a/src/distutils2/tests/test_pypi_versions.py b/src/distutils2/tests/test_pypi_versions.py
--- a/src/distutils2/tests/test_pypi_versions.py
+++ b/src/distutils2/tests/test_pypi_versions.py
@@ -19,6 +19,7 @@
 import os.path
 
 from distutils2.version import suggest_normalized_version
+from distutils2.tests import run_unittest
 from distutils2.tests.support import unittest
 
 def test_pypi():
@@ -52,7 +53,7 @@
         print "Saving package info..."
         f = open(INDEX_PICKLE_FILE, 'wb')
         try:
-            pickle.dump(package_info, o)
+            pickle.dump(package_info, f)
         finally:
             f.close()
 
diff --git a/src/distutils2/tests/test_register.py b/src/distutils2/tests/test_register.py
--- a/src/distutils2/tests/test_register.py
+++ b/src/distutils2/tests/test_register.py
@@ -124,7 +124,7 @@
 
         # with the content similar to WANTED_PYPIRC
         content = open(self.rc).read()
-        self.assertEquals(content, WANTED_PYPIRC)
+        self.assertEqual(content, WANTED_PYPIRC)
 
         # now let's make sure the .pypirc file generated
         # really works : we shouldn't be asked anything
@@ -141,7 +141,7 @@
         self.assertTrue(self.conn.reqs, 2)
         req1 = dict(self.conn.reqs[0].headers)
         req2 = dict(self.conn.reqs[1].headers)
-        self.assertEquals(req2['Content-length'], req1['Content-length'])
+        self.assertEqual(req2['Content-length'], req1['Content-length'])
         self.assertTrue('xxx' in self.conn.reqs[1].data)
 
     def test_password_not_in_file(self):
@@ -154,7 +154,7 @@
 
         # dist.password should be set
         # therefore used afterwards by other commands
-        self.assertEquals(cmd.distribution.password, 'password')
+        self.assertEqual(cmd.distribution.password, 'password')
 
     def test_registering(self):
         # this test runs choice 2
@@ -171,7 +171,7 @@
         self.assertTrue(self.conn.reqs, 1)
         req = self.conn.reqs[0]
         headers = dict(req.headers)
-        self.assertEquals(headers['Content-length'], '608')
+        self.assertEqual(headers['Content-length'], '608')
         self.assertTrue('tarek' in req.data)
 
     def test_password_reset(self):
@@ -189,7 +189,7 @@
         self.assertTrue(self.conn.reqs, 1)
         req = self.conn.reqs[0]
         headers = dict(req.headers)
-        self.assertEquals(headers['Content-length'], '290')
+        self.assertEqual(headers['Content-length'], '290')
         self.assertTrue('tarek' in req.data)
 
     @unittest.skipUnless(DOCUTILS_SUPPORT, 'needs docutils')
@@ -246,8 +246,8 @@
         cmd.ensure_finalized()
         cmd.distribution.metadata['Requires-Dist'] = ['lxml']
         data = cmd.build_post_data('submit')
-        self.assertEquals(data['metadata_version'], '1.2')
-        self.assertEquals(data['requires_dist'], ['lxml'])
+        self.assertEqual(data['metadata_version'], '1.2')
+        self.assertEqual(data['requires_dist'], ['lxml'])
 
 def test_suite():
     return unittest.makeSuite(RegisterTestCase)
diff --git a/src/distutils2/tests/test_sdist.py b/src/distutils2/tests/test_sdist.py
--- a/src/distutils2/tests/test_sdist.py
+++ b/src/distutils2/tests/test_sdist.py
@@ -20,7 +20,6 @@
 
 from os.path import join
 import sys
-import tempfile
 import warnings
 
 from distutils2.tests import captured_stdout
@@ -31,7 +30,7 @@
 from distutils2.tests.support import unittest
 from distutils2.tests.test_config import PyPIRCCommandTestCase
 from distutils2.errors import DistutilsExecError, DistutilsOptionError
-from distutils2.spawn import find_executable
+from distutils2.util import find_executable
 from distutils2.tests import support
 from distutils2.log import WARN
 try:
@@ -128,7 +127,7 @@
         # now let's check what we have
         dist_folder = join(self.tmp_dir, 'dist')
         files = os.listdir(dist_folder)
-        self.assertEquals(files, ['fake-1.0.zip'])
+        self.assertEqual(files, ['fake-1.0.zip'])
 
         zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip'))
         try:
@@ -137,7 +136,7 @@
             zip_file.close()
 
         # making sure everything has been pruned correctly
-        self.assertEquals(len(content), 4)
+        self.assertEqual(len(content), 4)
 
     @unittest.skipUnless(zlib, "requires zlib")
     def test_make_distribution(self):
@@ -159,7 +158,7 @@
         dist_folder = join(self.tmp_dir, 'dist')
         result = os.listdir(dist_folder)
         result.sort()
-        self.assertEquals(result,
+        self.assertEqual(result,
                           ['fake-1.0.tar', 'fake-1.0.tar.gz'] )
 
         os.remove(join(dist_folder, 'fake-1.0.tar'))
@@ -173,7 +172,7 @@
 
         result = os.listdir(dist_folder)
         result.sort()
-        self.assertEquals(result,
+        self.assertEqual(result,
                 ['fake-1.0.tar', 'fake-1.0.tar.gz'])
 
     @unittest.skipUnless(zlib, "requires zlib")
@@ -223,7 +222,7 @@
         # now let's check what we have
         dist_folder = join(self.tmp_dir, 'dist')
         files = os.listdir(dist_folder)
-        self.assertEquals(files, ['fake-1.0.zip'])
+        self.assertEqual(files, ['fake-1.0.zip'])
 
         zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip'))
         try:
@@ -232,11 +231,11 @@
             zip_file.close()
 
         # making sure everything was added
-        self.assertEquals(len(content), 11)
+        self.assertEqual(len(content), 11)
 
         # checking the MANIFEST
         manifest = open(join(self.tmp_dir, 'MANIFEST')).read()
-        self.assertEquals(manifest, MANIFEST % {'sep': os.sep})
+        self.assertEqual(manifest, MANIFEST % {'sep': os.sep})
 
     @unittest.skipUnless(zlib, "requires zlib")
     def test_metadata_check_option(self):
@@ -248,7 +247,7 @@
         cmd.ensure_finalized()
         cmd.run()
         warnings = self.get_logs(WARN)
-        self.assertEquals(len(warnings), 1)
+        self.assertEqual(len(warnings), 1)
 
         # trying with a complete set of metadata
         self.clear_logs()
@@ -260,7 +259,7 @@
         # removing manifest generated warnings
         warnings = [warn for warn in warnings if
                     not warn.endswith('-- skipping')]
-        self.assertEquals(len(warnings), 0)
+        self.assertEqual(len(warnings), 0)
 
 
     def test_show_formats(self):
@@ -270,7 +269,7 @@
         num_formats = len(get_archive_formats())
         output = [line for line in stdout.split('\n')
                   if line.strip().startswith('--formats=')]
-        self.assertEquals(len(output), num_formats)
+        self.assertEqual(len(output), num_formats)
 
     def test_finalize_options(self):
 
@@ -278,9 +277,9 @@
         cmd.finalize_options()
 
         # default options set by finalize
-        self.assertEquals(cmd.manifest, 'MANIFEST')
-        self.assertEquals(cmd.template, 'MANIFEST.in')
-        self.assertEquals(cmd.dist_dir, 'dist')
+        self.assertEqual(cmd.manifest, 'MANIFEST')
+        self.assertEqual(cmd.template, 'MANIFEST.in')
+        self.assertEqual(cmd.dist_dir, 'dist')
 
         # formats has to be a string splitable on (' ', ',') or
         # a stringlist
@@ -317,8 +316,8 @@
         archive = tarfile.open(archive_name)
         try:
             for member in archive.getmembers():
-                self.assertEquals(member.uid, 0)
-                self.assertEquals(member.gid, 0)
+                self.assertEqual(member.uid, 0)
+                self.assertEqual(member.gid, 0)
         finally:
             archive.close()
 
@@ -339,7 +338,7 @@
         # rights (see #7408)
         try:
             for member in archive.getmembers():
-                self.assertEquals(member.uid, os.getuid())
+                self.assertEqual(member.uid, os.getuid())
         finally:
             archive.close()
 
diff --git a/src/distutils2/tests/test_spawn.py b/src/distutils2/tests/test_spawn.py
deleted file mode 100644
--- a/src/distutils2/tests/test_spawn.py
+++ /dev/null
@@ -1,60 +0,0 @@
-"""Tests for distutils.spawn."""
-import os
-import time
-from distutils2.tests import captured_stdout
-
-from distutils2.spawn import _nt_quote_args
-from distutils2.spawn import spawn, find_executable
-from distutils2.errors import DistutilsExecError
-from distutils2.tests import support
-from distutils2.tests.support import unittest
-
-class SpawnTestCase(support.TempdirManager,
-                    support.LoggingSilencer,
-                    unittest.TestCase):
-
-    def test_nt_quote_args(self):
-
-        for (args, wanted) in ((['with space', 'nospace'],
-                                ['"with space"', 'nospace']),
-                               (['nochange', 'nospace'],
-                                ['nochange', 'nospace'])):
-            res = _nt_quote_args(args)
-            self.assertEquals(res, wanted)
-
-
-    @unittest.skipUnless(os.name in ('nt', 'posix'),
-                         'Runs only under posix or nt')
-    def test_spawn(self):
-        tmpdir = self.mkdtemp()
-
-        # creating something executable
-        # through the shell that returns 1
-        if os.name == 'posix':
-            exe = os.path.join(tmpdir, 'foo.sh')
-            self.write_file(exe, '#!/bin/sh\nexit 1')
-            os.chmod(exe, 0777)
-        else:
-            exe = os.path.join(tmpdir, 'foo.bat')
-            self.write_file(exe, 'exit 1')
-
-        os.chmod(exe, 0777)
-        self.assertRaises(DistutilsExecError, spawn, [exe])
-
-        # now something that works
-        if os.name == 'posix':
-            exe = os.path.join(tmpdir, 'foo.sh')
-            self.write_file(exe, '#!/bin/sh\nexit 0')
-            os.chmod(exe, 0777)
-        else:
-            exe = os.path.join(tmpdir, 'foo.bat')
-            self.write_file(exe, 'exit 0')
-
-        os.chmod(exe, 0777)
-        spawn([exe])  # should work without any error
-
-def test_suite():
-    return unittest.makeSuite(SpawnTestCase)
-
-if __name__ == "__main__":
-    unittest.main(defaultTest="test_suite")
diff --git a/src/distutils2/tests/test_upload.py b/src/distutils2/tests/test_upload.py
--- a/src/distutils2/tests/test_upload.py
+++ b/src/distutils2/tests/test_upload.py
@@ -1,12 +1,13 @@
 """Tests for distutils.command.upload."""
 # -*- encoding: utf8 -*-
-import os, sys
+import os
+import sys
 
 from distutils2.command.upload import upload
 from distutils2.core import Distribution
 
+from distutils2.tests import support
 from distutils2.tests.pypi_server import PyPIServer, PyPIServerTestCase
-from distutils2.tests import support
 from distutils2.tests.support import unittest
 from distutils2.tests.test_config import PYPIRC, PyPIRCCommandTestCase
 
@@ -29,10 +30,10 @@
         dist = Distribution()
         cmd = upload(dist)
         cmd.finalize_options()
-        for attr, waited in (('username', 'me'), ('password', 'secret'),
-                             ('realm', 'pypi'),
-                             ('repository', 'http://pypi.python.org/pypi')):
-            self.assertEqual(getattr(cmd, attr), waited)
+        for attr, expected in (('username', 'me'), ('password', 'secret'),
+                               ('realm', 'pypi'),
+                               ('repository', 'http://pypi.python.org/pypi')):
+            self.assertEqual(getattr(cmd, attr), expected)
 
     def test_saved_password(self):
         # file with no password
diff --git a/src/distutils2/tests/test_util.py b/src/distutils2/tests/test_util.py
--- a/src/distutils2/tests/test_util.py
+++ b/src/distutils2/tests/test_util.py
@@ -4,16 +4,21 @@
 from copy import copy
 from StringIO import StringIO
 import subprocess
+import tempfile
+import time
 
+from distutils2.tests import captured_stdout
+from distutils2.tests.support import unittest
 from distutils2.errors import (DistutilsPlatformError,
                                DistutilsByteCompileError,
-                               DistutilsFileError)
-
+                               DistutilsFileError,
+                               DistutilsExecError)
 from distutils2.util import (convert_path, change_root,
-                            check_environ, split_quoted, strtobool,
-                            rfc822_escape, get_compiler_versions,
-                            _find_exe_version, _MAC_OS_X_LD_VERSION,
-                            byte_compile, find_packages)
+                             check_environ, split_quoted, strtobool,
+                             rfc822_escape, get_compiler_versions,
+                             _find_exe_version, _MAC_OS_X_LD_VERSION,
+                             byte_compile, find_packages, spawn, find_executable,
+                             _nt_quote_args)
 from distutils2 import util
 from distutils2.tests import support
 from distutils2.tests.support import unittest
@@ -100,7 +105,7 @@
             return '/'.join(path)
         os.path.join = _join
 
-        self.assertEquals(convert_path('/home/to/my/stuff'),
+        self.assertEqual(convert_path('/home/to/my/stuff'),
                           '/home/to/my/stuff')
 
         # win
@@ -112,9 +117,9 @@
         self.assertRaises(ValueError, convert_path, '/home/to/my/stuff')
         self.assertRaises(ValueError, convert_path, 'home/to/my/stuff/')
 
-        self.assertEquals(convert_path('home/to/my/stuff'),
+        self.assertEqual(convert_path('home/to/my/stuff'),
                           'home\\to\\my\\stuff')
-        self.assertEquals(convert_path('.'),
+        self.assertEqual(convert_path('.'),
                           os.curdir)
 
     def test_change_root(self):
@@ -127,9 +132,9 @@
             return '/'.join(path)
         os.path.join = _join
 
-        self.assertEquals(change_root('/root', '/old/its/here'),
+        self.assertEqual(change_root('/root', '/old/its/here'),
                           '/root/old/its/here')
-        self.assertEquals(change_root('/root', 'its/here'),
+        self.assertEqual(change_root('/root', 'its/here'),
                           '/root/its/here')
 
         # windows
@@ -146,9 +151,9 @@
             return '\\'.join(path)
         os.path.join = _join
 
-        self.assertEquals(change_root('c:\\root', 'c:\\old\\its\\here'),
+        self.assertEqual(change_root('c:\\root', 'c:\\old\\its\\here'),
                           'c:\\root\\old\\its\\here')
-        self.assertEquals(change_root('c:\\root', 'its\\here'),
+        self.assertEqual(change_root('c:\\root', 'its\\here'),
                           'c:\\root\\its\\here')
 
         # BugsBunny os (it's a great os)
@@ -159,7 +164,7 @@
         # XXX platforms to be covered: os2, mac
 
     def test_split_quoted(self):
-        self.assertEquals(split_quoted('""one"" "two" \'three\' \\four'),
+        self.assertEqual(split_quoted('""one"" "two" \'three\' \\four'),
                           ['one', 'two', 'three', 'four'])
 
     def test_strtobool(self):
@@ -177,7 +182,7 @@
         res = rfc822_escape(header)
         wanted = ('I am a%(8s)spoor%(8s)slonesome%(8s)s'
                   'header%(8s)s') % {'8s': '\n'+8*' '}
-        self.assertEquals(res, wanted)
+        self.assertEqual(res, wanted)
 
     def test_find_exe_version(self):
         # the ld version scheme under MAC OS is:
@@ -195,7 +200,7 @@
                                 ('@(#)PROGRAM:ld  PROJECT:ld64-95.2.12',
                                  '95.2.12')):
             result = _MAC_OS_X_LD_VERSION.search(output)
-            self.assertEquals(result.group(1), version)
+            self.assertEqual(result.group(1), version)
 
     def _find_executable(self, name):
         if name in self._exes:
@@ -205,43 +210,43 @@
     def test_get_compiler_versions(self):
         # get_versions calls distutils.spawn.find_executable on
         # 'gcc', 'ld' and 'dllwrap'
-        self.assertEquals(get_compiler_versions(), (None, None, None))
+        self.assertEqual(get_compiler_versions(), (None, None, None))
 
         # Let's fake we have 'gcc' and it returns '3.4.5'
         self._exes['gcc'] = 'gcc (GCC) 3.4.5 (mingw special)\nFSF'
         res = get_compiler_versions()
-        self.assertEquals(str(res[0]), '3.4.5')
+        self.assertEqual(str(res[0]), '3.4.5')
 
         # and let's see what happens when the version
         # doesn't match the regular expression
         # (\d+\.\d+(\.\d+)*)
         self._exes['gcc'] = 'very strange output'
         res = get_compiler_versions()
-        self.assertEquals(res[0], None)
+        self.assertEqual(res[0], None)
 
         # same thing for ld
         if sys.platform != 'darwin':
             self._exes['ld'] = 'GNU ld version 2.17.50 20060824'
             res = get_compiler_versions()
-            self.assertEquals(str(res[1]), '2.17.50')
+            self.assertEqual(str(res[1]), '2.17.50')
             self._exes['ld'] = '@(#)PROGRAM:ld  PROJECT:ld64-77'
             res = get_compiler_versions()
-            self.assertEquals(res[1], None)
+            self.assertEqual(res[1], None)
         else:
             self._exes['ld'] = 'GNU ld version 2.17.50 20060824'
             res = get_compiler_versions()
-            self.assertEquals(res[1], None)
+            self.assertEqual(res[1], None)
             self._exes['ld'] = '@(#)PROGRAM:ld  PROJECT:ld64-77'
             res = get_compiler_versions()
-            self.assertEquals(str(res[1]), '77')
+            self.assertEqual(str(res[1]), '77')
 
         # and dllwrap
         self._exes['dllwrap'] = 'GNU dllwrap 2.17.50 20060824\nFSF'
         res = get_compiler_versions()
-        self.assertEquals(str(res[2]), '2.17.50')
+        self.assertEqual(str(res[2]), '2.17.50')
         self._exes['dllwrap'] = 'Cheese Wrap'
         res = get_compiler_versions()
-        self.assertEquals(res[2], None)
+        self.assertEqual(res[2], None)
 
     @unittest.skipUnless(hasattr(sys, 'dont_write_bytecode'),
                           'no dont_write_bytecode support')
@@ -257,7 +262,10 @@
 
     def test_newer(self):
         self.assertRaises(DistutilsFileError, util.newer, 'xxx', 'xxx')
-
+        self.newer_f1 = tempfile.NamedTemporaryFile()
+        time.sleep(1)
+        self.newer_f2 = tempfile.NamedTemporaryFile()
+        self.assertTrue(util.newer(self.newer_f2.name, self.newer_f1.name))
 
     def test_find_packages(self):
         # let's create a structure we want to scan:
@@ -294,7 +302,78 @@
         self.write_file(os.path.join(pkg5, '__init__.py'))
 
         res = find_packages([root], ['pkg1.pkg2'])
-        self.assertEquals(set(res), set(['pkg1', 'pkg5', 'pkg1.pkg3', 'pkg1.pkg3.pkg6']))
+        self.assertEqual(set(res), set(['pkg1', 'pkg5', 'pkg1.pkg3', 'pkg1.pkg3.pkg6']))
+
+    @unittest.skipUnless(sys.version > '2.6', 'Need Python 2.6 or more')
+    def test_run_2to3_on_code(self):
+        content = "print 'test'"
+        converted_content = "print('test')"
+        file_handle = self.mktempfile()
+        file_name = file_handle.name
+        file_handle.write(content)
+        file_handle.flush()
+        file_handle.seek(0)
+        from distutils2.util import run_2to3
+        run_2to3([file_name])
+        new_content = "".join(file_handle.read())
+        file_handle.close()
+        self.assertEquals(new_content, converted_content)
+
+    @unittest.skipUnless(sys.version > '2.6', 'Need Python 2.6 or more')
+    def test_run_2to3_on_doctests(self):
+        # to check if text files containing doctests only get converted.
+        content = ">>> print 'test'\ntest\n"
+        converted_content = ">>> print('test')\ntest\n\n"
+        file_handle = self.mktempfile()
+        file_name = file_handle.name
+        file_handle.write(content)
+        file_handle.flush()
+        file_handle.seek(0)
+        from distutils2.util import run_2to3
+        run_2to3([file_name], doctests_only=True)
+        new_content = "".join(file_handle.readlines())
+        file_handle.close()
+        self.assertEquals(new_content, converted_content)
+
+    def test_nt_quote_args(self):
+
+        for (args, wanted) in ((['with space', 'nospace'],
+                                ['"with space"', 'nospace']),
+                               (['nochange', 'nospace'],
+                                ['nochange', 'nospace'])):
+            res = _nt_quote_args(args)
+            self.assertEqual(res, wanted)
+
+
+    @unittest.skipUnless(os.name in ('nt', 'posix'),
+                         'Runs only under posix or nt')
+    def test_spawn(self):
+        tmpdir = self.mkdtemp()
+
+        # creating something executable
+        # through the shell that returns 1
+        if os.name == 'posix':
+            exe = os.path.join(tmpdir, 'foo.sh')
+            self.write_file(exe, '#!/bin/sh\nexit 1')
+            os.chmod(exe, 0777)
+        else:
+            exe = os.path.join(tmpdir, 'foo.bat')
+            self.write_file(exe, 'exit 1')
+
+        os.chmod(exe, 0777)
+        self.assertRaises(DistutilsExecError, spawn, [exe])
+
+        # now something that works
+        if os.name == 'posix':
+            exe = os.path.join(tmpdir, 'foo.sh')
+            self.write_file(exe, '#!/bin/sh\nexit 0')
+            os.chmod(exe, 0777)
+        else:
+            exe = os.path.join(tmpdir, 'foo.bat')
+            self.write_file(exe, 'exit 0')
+
+        os.chmod(exe, 0777)
+        spawn([exe])  # should work without any error
 
 
 def test_suite():
diff --git a/src/distutils2/tests/test_version.py b/src/distutils2/tests/test_version.py
--- a/src/distutils2/tests/test_version.py
+++ b/src/distutils2/tests/test_version.py
@@ -3,7 +3,7 @@
 import os
 
 from distutils2.version import NormalizedVersion as V
-from distutils2.version import IrrationalVersionError
+from distutils2.version import HugeMajorVersionNumError, IrrationalVersionError
 from distutils2.version import suggest_normalized_version as suggest
 from distutils2.version import VersionPredicate
 from distutils2.tests.support import unittest
@@ -22,18 +22,22 @@
                 (V('1.0.dev345'), '1.0.dev345'),
                 (V('1.0.post456.dev623'), '1.0.post456.dev623'))
 
+    def test_repr(self):
+
+        self.assertEqual(repr(V('1.0')), "NormalizedVersion('1.0')")
+
     def test_basic_versions(self):
 
         for v, s in self.versions:
-            self.assertEquals(str(v), s)
+            self.assertEqual(str(v), s)
 
     def test_from_parts(self):
 
         for v, s in self.versions:
             parts = v.parts
             v2 = V.from_parts(*v.parts)
-            self.assertEquals(v, v2)
-            self.assertEquals(str(v), str(v2))
+            self.assertEqual(v, v2)
+            self.assertEqual(str(v), str(v2))
 
     def test_irrational_versions(self):
 
@@ -44,6 +48,12 @@
         for s in irrational:
             self.assertRaises(IrrationalVersionError, V, s)
 
+    def test_huge_version(self):
+
+        self.assertEquals(str(V('1980.0')), '1980.0')
+        self.assertRaises(HugeMajorVersionNumError, V, '1981.0')
+        self.assertEquals(str(V('1981.0', error_on_huge_major_num=False)), '1981.0')
+
     def test_comparison(self):
         r"""
         >>> V('1.2.0') == '1.2'
@@ -51,12 +61,33 @@
         ...
         TypeError: cannot compare NormalizedVersion and str
 
+        >>> V('1.2') < '1.3'
+        Traceback (most recent call last):
+        ...
+        TypeError: cannot compare NormalizedVersion and str
+
         >>> V('1.2.0') == V('1.2')
         True
         >>> V('1.2.0') == V('1.2.3')
         False
+        >>> V('1.2.0') != V('1.2.3')
+        True
         >>> V('1.2.0') < V('1.2.3')
         True
+        >>> V('1.2.0') < V('1.2.0')
+        False
+        >>> V('1.2.0') <= V('1.2.0')
+        True
+        >>> V('1.2.0') <= V('1.2.3')
+        True
+        >>> V('1.2.3') <= V('1.2.0')
+        False
+        >>> V('1.2.0') >= V('1.2.0')
+        True
+        >>> V('1.2.3') >= V('1.2.0')
+        True
+        >>> V('1.2.0') >= V('1.2.3')
+        False
         >>> (V('1.0') > V('1.0b2'))
         True
         >>> (V('1.0') > V('1.0c2') > V('1.0c1') > V('1.0b2') > V('1.0b1')
@@ -96,34 +127,35 @@
 
     def test_suggest_normalized_version(self):
 
-        self.assertEquals(suggest('1.0'), '1.0')
-        self.assertEquals(suggest('1.0-alpha1'), '1.0a1')
-        self.assertEquals(suggest('1.0c2'), '1.0c2')
-        self.assertEquals(suggest('walla walla washington'), None)
-        self.assertEquals(suggest('2.4c1'), '2.4c1')
+        self.assertEqual(suggest('1.0'), '1.0')
+        self.assertEqual(suggest('1.0-alpha1'), '1.0a1')
+        self.assertEqual(suggest('1.0c2'), '1.0c2')
+        self.assertEqual(suggest('walla walla washington'), None)
+        self.assertEqual(suggest('2.4c1'), '2.4c1')
+        self.assertEqual(suggest('v1.0'), '1.0')
 
         # from setuptools
-        self.assertEquals(suggest('0.4a1.r10'), '0.4a1.post10')
-        self.assertEquals(suggest('0.7a1dev-r66608'), '0.7a1.dev66608')
-        self.assertEquals(suggest('0.6a9.dev-r41475'), '0.6a9.dev41475')
-        self.assertEquals(suggest('2.4preview1'), '2.4c1')
-        self.assertEquals(suggest('2.4pre1') , '2.4c1')
-        self.assertEquals(suggest('2.1-rc2'), '2.1c2')
+        self.assertEqual(suggest('0.4a1.r10'), '0.4a1.post10')
+        self.assertEqual(suggest('0.7a1dev-r66608'), '0.7a1.dev66608')
+        self.assertEqual(suggest('0.6a9.dev-r41475'), '0.6a9.dev41475')
+        self.assertEqual(suggest('2.4preview1'), '2.4c1')
+        self.assertEqual(suggest('2.4pre1') , '2.4c1')
+        self.assertEqual(suggest('2.1-rc2'), '2.1c2')
 
         # from pypi
-        self.assertEquals(suggest('0.1dev'), '0.1.dev0')
-        self.assertEquals(suggest('0.1.dev'), '0.1.dev0')
+        self.assertEqual(suggest('0.1dev'), '0.1.dev0')
+        self.assertEqual(suggest('0.1.dev'), '0.1.dev0')
 
         # we want to be able to parse Twisted
         # development versions are like post releases in Twisted
-        self.assertEquals(suggest('9.0.0+r2363'), '9.0.0.post2363')
+        self.assertEqual(suggest('9.0.0+r2363'), '9.0.0.post2363')
 
         # pre-releases are using markers like "pre1"
-        self.assertEquals(suggest('9.0.0pre1'), '9.0.0c1')
+        self.assertEqual(suggest('9.0.0pre1'), '9.0.0c1')
 
         # we want to be able to parse Tcl-TK
         # they us "p1" "p2" for post releases
-        self.assertEquals(suggest('1.4p1'), '1.4.post1')
+        self.assertEqual(suggest('1.4p1'), '1.4.post1')
 
     def test_predicate(self):
         # VersionPredicate knows how to parse stuff like:
@@ -151,15 +183,37 @@
         self.assertFalse(VersionPredicate('Hey (<=2.5)').match('2.6.0'))
         self.assertTrue(VersionPredicate('Hey (>=2.5)').match('2.5.1'))
 
+        self.assertRaises(ValueError, VersionPredicate, '')
+
         # XXX need to silent the micro version in this case
         #assert not VersionPredicate('Ho (<3.0,!=2.6)').match('2.6.3')
 
+    def test_is_final(self):
+        # VersionPredicate knows is a distribution is a final one or not.
+        final_versions = ('1.0', '1.0.post456')
+        other_versions = ('1.0.dev1', '1.0a2', '1.0c3')
+
+        for version in final_versions:
+            self.assertTrue(V(version).is_final)
+        for version in other_versions:
+            self.assertFalse(V(version).is_final)
+
+class VersionWhiteBoxTestCase(unittest.TestCase):
+
+    def test_parse_numdots(self):
+        # For code coverage completeness, as pad_zeros_length can't be set or
+        # influenced from the public interface
+        self.assertEquals(V('1.0')._parse_numdots('1.0', '1.0',
+                                                  pad_zeros_length=3),
+                          [1, 0, 0])
+
+
 def test_suite():
     #README = os.path.join(os.path.dirname(__file__), 'README.txt')
     #suite = [doctest.DocFileSuite(README), unittest.makeSuite(VersionTestCase)]
-    suite = [unittest.makeSuite(VersionTestCase)]
+    suite = [unittest.makeSuite(VersionTestCase),
+             unittest.makeSuite(VersionWhiteBoxTestCase)]
     return unittest.TestSuite(suite)
 
 if __name__ == "__main__":
     unittest.main(defaultTest="test_suite")
-
diff --git a/src/distutils2/util.py b/src/distutils2/util.py
--- a/src/distutils2/util.py
+++ b/src/distutils2/util.py
@@ -1,22 +1,25 @@
 """distutils.util
 
-Miscellaneous utility functions -- anything that doesn't fit into
-one of the other *util.py modules.
+Miscellaneous utility functions.
 """
 
 __revision__ = "$Id: util.py 77761 2010-01-26 22:46:15Z tarek.ziade $"
 
-import sys, os, string, re
+import sys
+import os
+import string
+import re
+from copy import copy
 from fnmatch import fnmatchcase
 
 from distutils2.errors import (DistutilsPlatformError, DistutilsFileError,
-                               DistutilsByteCompileError)
-from distutils2.spawn import spawn, find_executable
+                               DistutilsByteCompileError, DistutilsExecError)
 from distutils2 import log
 from distutils2._backport import sysconfig as _sysconfig
 
 _PLATFORM = None
 
+
 def newer(source, target):
     """Tells if the target is newer than the source.
 
@@ -37,6 +40,7 @@
 
     return os.stat(source).st_mtime > os.stat(target).st_mtime
 
+
 def get_platform():
     """Return a string that identifies the current platform.
 
@@ -48,6 +52,7 @@
         _PLATFORM = _sysconfig.get_platform()
     return _PLATFORM
 
+
 def set_platform(identifier):
     """Sets the platform string identifier returned by get_platform().
 
@@ -57,6 +62,7 @@
     global _PLATFORM
     _PLATFORM = identifier
 
+
 def convert_path(pathname):
     """Return 'pathname' as a name that will work on the native filesystem.
 
@@ -125,6 +131,7 @@
 
 _environ_checked = 0
 
+
 def check_environ():
     """Ensure that 'os.environ' has all the environment variables needed.
 
@@ -147,6 +154,7 @@
 
     _environ_checked = 1
 
+
 def subst_vars(s, local_vars):
     """Perform shell/Perl-style variable substitution on 'string'.
 
@@ -158,7 +166,8 @@
     variables not found in either 'local_vars' or 'os.environ'.
     """
     check_environ()
-    def _subst (match, local_vars=local_vars):
+
+    def _subst(match, local_vars=local_vars):
         var_name = match.group(1)
         if var_name in local_vars:
             return str(local_vars[var_name])
@@ -170,6 +179,7 @@
     except KeyError, var:
         raise ValueError("invalid variable '$%s'" % var)
 
+
 def grok_environment_error(exc, prefix="error: "):
     """Generate a useful error message from an EnvironmentError.
 
@@ -196,12 +206,14 @@
 # Needed by 'split_quoted()'
 _wordchars_re = _squote_re = _dquote_re = None
 
+
 def _init_regex():
     global _wordchars_re, _squote_re, _dquote_re
     _wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace)
     _squote_re = re.compile(r"'(?:[^'\\]|\\.)*'")
     _dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"')
 
+
 def split_quoted(s):
     """Split a string up according to Unix shell-like rules for quotes and
     backslashes.
@@ -217,7 +229,8 @@
     # This is a nice algorithm for splitting up a single string, since it
     # doesn't require character-by-character examination.  It was a little
     # bit of a brain-bender to get it working right, though...
-    if _wordchars_re is None: _init_regex()
+    if _wordchars_re is None:
+        _init_regex()
 
     s = s.strip()
     words = []
@@ -237,8 +250,8 @@
 
         elif s[end] == '\\':            # preserve whatever is being escaped;
                                         # will become part of the current word
-            s = s[:end] + s[end+1:]
-            pos = end+1
+            s = s[:end] + s[end + 1:]
+            pos = end + 1
 
         else:
             if s[end] == "'":           # slurp singly-quoted string
@@ -253,7 +266,7 @@
                 raise ValueError("bad string (mismatched %s quotes?)" % s[end])
 
             (beg, end) = m.span()
-            s = s[:beg] + s[beg+1:end-1] + s[end:]
+            s = s[:beg] + s[beg + 1:end - 1] + s[end:]
             pos = m.end() - 2
 
         if pos >= len(s):
@@ -296,7 +309,7 @@
     elif val in ('n', 'no', 'f', 'false', 'off', '0'):
         return 0
     else:
-        raise ValueError, "invalid truth value %r" % (val,)
+        raise ValueError("invalid truth value %r" % (val,))
 
 
 def byte_compile(py_files, optimize=0, force=0, prefix=None, base_dir=None,
@@ -350,12 +363,8 @@
     # "Indirect" byte-compilation: write a temporary script and then
     # run it with the appropriate flags.
     if not direct:
-        try:
-            from tempfile import mkstemp
-            (script_fd, script_name) = mkstemp(".py")
-        except ImportError:
-            from tempfile import mktemp
-            (script_fd, script_name) = None, mktemp(".py")
+        from tempfile import mkstemp
+        script_fd, script_name = mkstemp(".py")
         log.info("writing byte-compilation script '%s'", script_name)
         if not dry_run:
             if script_fd is not None:
@@ -363,43 +372,50 @@
             else:
                 script = open(script_name, "w")
 
-            script.write("""\
-from distutils.util import byte_compile
+            try:
+                script.write("""\
+from distutils2.util import byte_compile
 files = [
 """)
 
-            # XXX would be nice to write absolute filenames, just for
-            # safety's sake (script should be more robust in the face of
-            # chdir'ing before running it).  But this requires abspath'ing
-            # 'prefix' as well, and that breaks the hack in build_lib's
-            # 'byte_compile()' method that carefully tacks on a trailing
-            # slash (os.sep really) to make sure the prefix here is "just
-            # right".  This whole prefix business is rather delicate -- the
-            # problem is that it's really a directory, but I'm treating it
-            # as a dumb string, so trailing slashes and so forth matter.
+                # XXX would be nice to write absolute filenames, just for
+                # safety's sake (script should be more robust in the face of
+                # chdir'ing before running it).  But this requires abspath'ing
+                # 'prefix' as well, and that breaks the hack in build_lib's
+                # 'byte_compile()' method that carefully tacks on a trailing
+                # slash (os.sep really) to make sure the prefix here is "just
+                # right".  This whole prefix business is rather delicate -- the
+                # problem is that it's really a directory, but I'm treating it
+                # as a dumb string, so trailing slashes and so forth matter.
 
-            #py_files = map(os.path.abspath, py_files)
-            #if prefix:
-            #    prefix = os.path.abspath(prefix)
+                #py_files = map(os.path.abspath, py_files)
+                #if prefix:
+                #    prefix = os.path.abspath(prefix)
 
-            script.write(",\n".join(map(repr, py_files)) + "]\n")
-            script.write("""
+                script.write(",\n".join(map(repr, py_files)) + "]\n")
+                script.write("""
 byte_compile(files, optimize=%r, force=%r,
              prefix=%r, base_dir=%r,
              verbose=%r, dry_run=0,
              direct=1)
 """ % (optimize, force, prefix, base_dir, verbose))
 
-            script.close()
+            finally:
+                script.close()
 
         cmd = [sys.executable, script_name]
         if optimize == 1:
             cmd.insert(1, "-O")
         elif optimize == 2:
             cmd.insert(1, "-OO")
-        spawn(cmd, dry_run=dry_run)
-        execute(os.remove, (script_name,), "removing %s" % script_name,
-                dry_run=dry_run)
+
+        env = copy(os.environ)
+        env['PYTHONPATH'] = ':'.join(sys.path)
+        try:
+            spawn(cmd, dry_run=dry_run, env=env)
+        finally:
+            execute(os.remove, (script_name,), "removing %s" % script_name,
+                    dry_run=dry_run)
 
     # "Direct" byte-compilation: use the py_compile module to compile
     # right here, right now.  Note that the script generated in indirect
@@ -447,7 +463,9 @@
     return sep.join(lines)
 
 _RE_VERSION = re.compile('(\d+\.\d+(\.\d+)*)')
-_MAC_OS_X_LD_VERSION = re.compile('^@\(#\)PROGRAM:ld  PROJECT:ld64-((\d+)(\.\d+)*)')
+_MAC_OS_X_LD_VERSION = re.compile('^@\(#\)PROGRAM:ld  '
+                                  'PROJECT:ld64-((\d+)(\.\d+)*)')
+
 
 def _find_ld_version():
     """Finds the ld version. The version scheme differs under Mac OSX."""
@@ -456,6 +474,7 @@
     else:
         return _find_exe_version('ld -v')
 
+
 def _find_exe_version(cmd, pattern=_RE_VERSION):
     """Find the version of an executable by running `cmd` in the shell.
 
@@ -485,6 +504,7 @@
         return None
     return result.group(1)
 
+
 def get_compiler_versions():
     """Returns a tuple providing the versions of gcc, ld and dllwrap
 
@@ -496,6 +516,7 @@
     dllwrap = _find_exe_version('dllwrap --version')
     return gcc, ld, dllwrap
 
+
 def newer_group(sources, target, missing='error'):
     """Return true if 'target' is out-of-date with respect to any file
     listed in 'sources'.
@@ -535,14 +556,18 @@
 
     return False
 
+
 def write_file(filename, contents):
     """Create a file with the specified name and write 'contents' (a
     sequence of strings without line terminators) to it.
     """
-    f = open(filename, "w")
-    for line in contents:
-        f.write(line + "\n")
-    f.close()
+    try:
+        f = open(filename, "w")
+        for line in contents:
+            f.write(line + "\n")
+    finally:
+        f.close()
+
 
 def _is_package(path):
     """Returns True if path is a package (a dir with an __init__ file."""
@@ -550,6 +575,7 @@
         return False
     return os.path.isfile(os.path.join(path, '__init__.py'))
 
+
 def _under(path, root):
     path = path.split(os.sep)
     root = root.split(os.sep)
@@ -560,12 +586,14 @@
             return False
     return True
 
+
 def _package_name(root_path, path):
     """Returns a dotted package name, given a subpath."""
     if not _under(path, root_path):
         raise ValueError('"%s" is not a subpath of "%s"' % (path, root_path))
     return path[len(root_path) + 1:].replace(os.sep, '.')
 
+
 def find_packages(paths=('.',), exclude=()):
     """Return a list all Python packages found recursively within
     directories 'paths'
@@ -580,6 +608,7 @@
     """
     packages = []
     discarded = []
+
     def _discarded(path):
         for discard in discarded:
             if _under(path, discard):
@@ -611,3 +640,232 @@
                 packages.append(package_name)
     return packages
 
+
+# utility functions for 2to3 support
+
+def run_2to3(files, doctests_only=False, fixer_names=None, options=None,
+                                                            explicit=None):
+    """ Wrapper function around the refactor() class which
+    performs the conversions on a list of python files.
+    Invoke 2to3 on a list of Python files. The files should all come
+    from the build area, as the modification is done in-place."""
+
+    if not files:
+        return
+
+    # Make this class local, to delay import of 2to3
+    from lib2to3.refactor import get_fixers_from_package
+    from distutils2.converter.refactor import DistutilsRefactoringTool
+
+    if fixer_names is None:
+        fixer_names = get_fixers_from_package('lib2to3.fixes')
+
+    r = DistutilsRefactoringTool(fixer_names, options=options)
+    if doctests_only:
+        r.refactor(files, doctests_only=True, write=True)
+    else:
+        r.refactor(files, write=True)
+
+
+class Mixin2to3:
+    """ Wrapper class for commands that run 2to3.
+    To configure 2to3, setup scripts may either change
+    the class variables, or inherit from this class
+    to override how 2to3 is invoked.
+    """
+    # provide list of fixers to run.
+    # defaults to all from lib2to3.fixers
+    fixer_names = None
+
+    # options dictionary
+    options = None
+
+    # list of fixers to invoke even though they are marked as explicit
+    explicit = None
+
+    def run_2to3(self, files, doctests_only=False):
+        """ Issues a call to util.run_2to3. """
+        return run_2to3(files, doctests_only, self.fixer_names,
+                        self.options, self.explicit)
+
+
+def spawn(cmd, search_path=1, verbose=0, dry_run=0, env=None):
+    """Run another program specified as a command list 'cmd' in a new process.
+
+    'cmd' is just the argument list for the new process, ie.
+    cmd[0] is the program to run and cmd[1:] are the rest of its arguments.
+    There is no way to run a program with a name different from that of its
+    executable.
+
+    If 'search_path' is true (the default), the system's executable
+    search path will be used to find the program; otherwise, cmd[0]
+    must be the exact path to the executable.  If 'dry_run' is true,
+    the command will not actually be run.
+
+    If 'env' is given, it's a environment dictionary used for the execution
+    environment.
+
+    Raise DistutilsExecError if running the program fails in any way; just
+    return on success.
+    """
+    if os.name == 'posix':
+        _spawn_posix(cmd, search_path, dry_run=dry_run, env=env)
+    elif os.name == 'nt':
+        _spawn_nt(cmd, search_path, dry_run=dry_run, env=env)
+    elif os.name == 'os2':
+        _spawn_os2(cmd, search_path, dry_run=dry_run, env=env)
+    else:
+        raise DistutilsPlatformError(
+              "don't know how to spawn programs on platform '%s'" % os.name)
+
+
+def _nt_quote_args(args):
+    """Quote command-line arguments for DOS/Windows conventions.
+
+    Just wraps every argument which contains blanks in double quotes, and
+    returns a new argument list.
+    """
+    # XXX this doesn't seem very robust to me -- but if the Windows guys
+    # say it'll work, I guess I'll have to accept it.  (What if an arg
+    # contains quotes?  What other magic characters, other than spaces,
+    # have to be escaped?  Is there an escaping mechanism other than
+    # quoting?)
+    for i, arg in enumerate(args):
+        if ' ' in arg:
+            args[i] = '"%s"' % arg
+    return args
+
+
+def _spawn_nt(cmd, search_path=1, verbose=0, dry_run=0, env=None):
+    executable = cmd[0]
+    cmd = _nt_quote_args(cmd)
+    if search_path:
+        # either we find one or it stays the same
+        executable = find_executable(executable) or executable
+    log.info(' '.join([executable] + cmd[1:]))
+    if not dry_run:
+        # spawn for NT requires a full path to the .exe
+        try:
+            if env is None:
+                rc = os.spawnv(os.P_WAIT, executable, cmd)
+            else:
+                rc = os.spawnve(os.P_WAIT, executable, cmd, env)
+
+        except OSError, exc:
+            # this seems to happen when the command isn't found
+            raise DistutilsExecError(
+                  "command '%s' failed: %s" % (cmd[0], exc[-1]))
+        if rc != 0:
+            # and this reflects the command running but failing
+            raise DistutilsExecError(
+                  "command '%s' failed with exit status %d" % (cmd[0], rc))
+
+
+def _spawn_os2(cmd, search_path=1, verbose=0, dry_run=0, env=None):
+    executable = cmd[0]
+    if search_path:
+        # either we find one or it stays the same
+        executable = find_executable(executable) or executable
+    log.info(' '.join([executable] + cmd[1:]))
+    if not dry_run:
+        # spawnv for OS/2 EMX requires a full path to the .exe
+        try:
+            if env is None:
+                rc = os.spawnv(os.P_WAIT, executable, cmd)
+            else:
+                rc = os.spawnve(os.P_WAIT, executable, cmd, env)
+
+        except OSError, exc:
+            # this seems to happen when the command isn't found
+            raise DistutilsExecError(
+                  "command '%s' failed: %s" % (cmd[0], exc[-1]))
+        if rc != 0:
+            # and this reflects the command running but failing
+            log.debug("command '%s' failed with exit status %d" % (cmd[0], rc))
+            raise DistutilsExecError(
+                  "command '%s' failed with exit status %d" % (cmd[0], rc))
+
+
+def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0, env=None):
+    log.info(' '.join(cmd))
+    if dry_run:
+        return
+
+    if env is None:
+        exec_fn = search_path and os.execvp or os.execv
+    else:
+        exec_fn = search_path and os.execvpe or os.execve
+
+    pid = os.fork()
+
+    if pid == 0:  # in the child
+        try:
+            if env is None:
+                exec_fn(cmd[0], cmd)
+            else:
+                exec_fn(cmd[0], cmd, env)
+        except OSError, e:
+            sys.stderr.write("unable to execute %s: %s\n" %
+                             (cmd[0], e.strerror))
+            os._exit(1)
+
+        sys.stderr.write("unable to execute %s for unknown reasons" % cmd[0])
+        os._exit(1)
+    else:   # in the parent
+        # Loop until the child either exits or is terminated by a signal
+        # (ie. keep waiting if it's merely stopped)
+        while 1:
+            try:
+                pid, status = os.waitpid(pid, 0)
+            except OSError, exc:
+                import errno
+                if exc.errno == errno.EINTR:
+                    continue
+                raise DistutilsExecError(
+                      "command '%s' failed: %s" % (cmd[0], exc[-1]))
+            if os.WIFSIGNALED(status):
+                raise DistutilsExecError(
+                      "command '%s' terminated by signal %d" % \
+                      (cmd[0], os.WTERMSIG(status)))
+
+            elif os.WIFEXITED(status):
+                exit_status = os.WEXITSTATUS(status)
+                if exit_status == 0:
+                    return   # hey, it succeeded!
+                else:
+                    raise DistutilsExecError(
+                          "command '%s' failed with exit status %d" % \
+                          (cmd[0], exit_status))
+
+            elif os.WIFSTOPPED(status):
+                continue
+
+            else:
+                raise DistutilsExecError(
+                      "unknown error executing '%s': termination status %d" % \
+                      (cmd[0], status))
+
+
+def find_executable(executable, path=None):
+    """Tries to find 'executable' in the directories listed in 'path'.
+
+    A string listing directories separated by 'os.pathsep'; defaults to
+    os.environ['PATH'].  Returns the complete filename or None if not found.
+    """
+    if path is None:
+        path = os.environ['PATH']
+    paths = path.split(os.pathsep)
+    base, ext = os.path.splitext(executable)
+
+    if (sys.platform == 'win32' or os.name == 'os2') and (ext != '.exe'):
+        executable = executable + '.exe'
+
+    if not os.path.isfile(executable):
+        for p in paths:
+            f = os.path.join(p, executable)
+            if os.path.isfile(f):
+                # the file exists, we have a shot at spawn working
+                return f
+        return None
+    else:
+        return executable
diff --git a/src/distutils2/version.py b/src/distutils2/version.py
--- a/src/distutils2/version.py
+++ b/src/distutils2/version.py
@@ -1,8 +1,11 @@
-import sys
 import re
 
 from distutils2.errors import IrrationalVersionError, HugeMajorVersionNumError
 
+__all__ = ['NormalizedVersion', 'suggest_normalized_version',
+           'VersionPredicate', 'is_valid_version', 'is_valid_versions',
+           'is_valid_predicate']
+
 # A marker used in the second and third parts of the `parts` tuple, for
 # versions that don't have those segments, to sort properly. An example
 # of versions in sort order ('highest' last):
@@ -18,9 +21,9 @@
 #                                                              |
 #   'dev' < 'f' ----------------------------------------------/
 # Other letters would do, but 'f' for 'final' is kind of nice.
-FINAL_MARKER = ('f',)
+_FINAL_MARKER = ('f',)
 
-VERSION_RE = re.compile(r'''
+_VERSION_RE = re.compile(r'''
     ^
     (?P<version>\d+\.\d+)          # minimum 'N.N'
     (?P<extraversion>(?:\.\d+)*)   # any number of extra '.N' segments
@@ -32,6 +35,7 @@
     (?P<postdev>(\.post(?P<post>\d+))?(\.dev(?P<dev>\d+))?)?
     $''', re.VERBOSE)
 
+
 class NormalizedVersion(object):
     """A rational version.
 
@@ -57,7 +61,8 @@
         @param error_on_huge_major_num {bool} Whether to consider an
             apparent use of a year or full date as the major version number
             an error. Default True. One of the observed patterns on PyPI before
-            the introduction of `NormalizedVersion` was version numbers like this:
+            the introduction of `NormalizedVersion` was version numbers like
+            this:
                 2009.01.03
                 20040603
                 2005.01
@@ -67,16 +72,17 @@
             the possibility of using a version number like "1.0" (i.e.
             where the major number is less than that huge major number).
         """
+        self.is_final = True  # by default, consider a version as final.
         self._parse(s, error_on_huge_major_num)
 
     @classmethod
-    def from_parts(cls, version, prerelease=FINAL_MARKER,
-                   devpost=FINAL_MARKER):
+    def from_parts(cls, version, prerelease=_FINAL_MARKER,
+                   devpost=_FINAL_MARKER):
         return cls(cls.parts_to_str((version, prerelease, devpost)))
 
     def _parse(self, s, error_on_huge_major_num=True):
         """Parses a string version into parts."""
-        match = VERSION_RE.search(s)
+        match = _VERSION_RE.search(s)
         if not match:
             raise IrrationalVersionError(s)
 
@@ -97,8 +103,9 @@
             block += self._parse_numdots(groups.get('prerelversion'), s,
                                          pad_zeros_length=1)
             parts.append(tuple(block))
+            self.is_final = False
         else:
-            parts.append(FINAL_MARKER)
+            parts.append(_FINAL_MARKER)
 
         # postdev
         if groups.get('postdev'):
@@ -106,14 +113,15 @@
             dev = groups.get('dev')
             postdev = []
             if post is not None:
-                postdev.extend([FINAL_MARKER[0], 'post', int(post)])
+                postdev.extend([_FINAL_MARKER[0], 'post', int(post)])
                 if dev is None:
-                    postdev.append(FINAL_MARKER[0])
+                    postdev.append(_FINAL_MARKER[0])
             if dev is not None:
                 postdev.extend(['dev', int(dev)])
+                self.is_final = False
             parts.append(tuple(postdev))
         else:
-            parts.append(FINAL_MARKER)
+            parts.append(_FINAL_MARKER)
         self.parts = tuple(parts)
         if error_on_huge_major_num and self.parts[0][0] > 1980:
             raise HugeMajorVersionNumError("huge major version number, %r, "
@@ -154,10 +162,10 @@
         # XXX This doesn't check for invalid tuples
         main, prerel, postdev = parts
         s = '.'.join(str(v) for v in main)
-        if prerel is not FINAL_MARKER:
+        if prerel is not _FINAL_MARKER:
             s += prerel[0]
             s += '.'.join(str(v) for v in prerel[1:])
-        if postdev and postdev is not FINAL_MARKER:
+        if postdev and postdev is not _FINAL_MARKER:
             if postdev[0] == 'f':
                 postdev = postdev[1:]
             i = 0
@@ -200,6 +208,7 @@
     # See http://docs.python.org/reference/datamodel#object.__hash__
     __hash__ = object.__hash__
 
+
 def suggest_normalized_version(s):
     """Suggest a normalized version close to the given version string.
 
@@ -211,7 +220,7 @@
     on observation of versions currently in use on PyPI. Given a dump of
     those version during PyCon 2009, 4287 of them:
     - 2312 (53.93%) match NormalizedVersion without change
-    - with the automatic suggestion
+      with the automatic suggestion
     - 3474 (81.04%) match when using this suggestion method
 
     @param s {str} An irrational version string.
@@ -301,7 +310,6 @@
     # PyPI stats: ~21 (0.62%) better
     rs = re.sub(r"\.?(pre|preview|-c)(\d+)$", r"c\g<2>", rs)
 
-
     # Tcl/Tk uses "px" for their post release markers
     rs = re.sub(r"p(\d+)$", r".post\1", rs)
 
@@ -318,6 +326,7 @@
 _PLAIN_VERSIONS = re.compile(r"^\s*(.*)\s*$")
 _SPLIT_CMP = re.compile(r"^\s*(<=|>=|<|>|!=|==)\s*([^\s,]+)\s*$")
 
+
 def _split_predicate(predicate):
     match = _SPLIT_CMP.match(predicate)
     if match is None:
@@ -364,26 +373,25 @@
                 return False
         return True
 
-class Versions(VersionPredicate):
+
+class _Versions(VersionPredicate):
     def __init__(self, predicate):
         predicate = predicate.strip()
         match = _PLAIN_VERSIONS.match(predicate)
-        if match is None:
-            raise ValueError('Bad predicate "%s"' % predicate)
         self.name = None
         predicates = match.groups()[0]
         self.predicates = [_split_predicate(pred.strip())
                            for pred in predicates.split(',')]
 
-class Version(VersionPredicate):
+
+class _Version(VersionPredicate):
     def __init__(self, predicate):
         predicate = predicate.strip()
         match = _PLAIN_VERSIONS.match(predicate)
-        if match is None:
-            raise ValueError('Bad predicate "%s"' % predicate)
         self.name = None
         self.predicates = _split_predicate(match.groups()[0])
 
+
 def is_valid_predicate(predicate):
     try:
         VersionPredicate(predicate)
@@ -392,19 +400,20 @@
     else:
         return True
 
+
 def is_valid_versions(predicate):
     try:
-        Versions(predicate)
+        _Versions(predicate)
     except (ValueError, IrrationalVersionError):
         return False
     else:
         return True
 
+
 def is_valid_version(predicate):
     try:
-        Version(predicate)
+        _Version(predicate)
     except (ValueError, IrrationalVersionError):
         return False
     else:
         return True
-
diff --git a/src/runtests-cov.py b/src/runtests-cov.py
new file mode 100755
--- /dev/null
+++ b/src/runtests-cov.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python
+"""Tests for distutils2.
+
+The tests for distutils2 are defined in the distutils2.tests package.
+"""
+
+import sys
+from os.path import dirname, islink, realpath
+from optparse import OptionParser
+
+def ignore_prefixes(module):
+    """ Return a list of prefixes to ignore in the coverage report if
+    we want to completely skip `module`.
+    """
+    # A function like that is needed because some GNU/Linux
+    # distributions, such a Ubuntu, really like to build link farm in
+    # /usr/lib in order to save a few bytes on the disk.
+    dirnames = [dirname(module.__file__)]
+    
+    pymod = module.__file__.rstrip("c")
+    if islink(pymod):
+        dirnames.append(dirname(realpath(pymod)))
+    return dirnames
+
+def parse_opts():
+    parser = OptionParser(usage="%prog [OPTIONS]",
+                          description="run the distutils2 unittests")
+    
+    parser.add_option("-q", "--quiet", help="do not print verbose messages",
+                      action="store_true", default=False)
+    parser.add_option("-c", "--coverage", action="store_true", default=False,
+                      help="produce a coverage report at the end of the run")
+    parser.add_option("-r", "--report", action="store_true", default=False,
+                      help="produce a coverage report from the last test run")
+    parser.add_option("-m", "--show-missing", action="store_true",
+                      default=False,
+                      help=("Show line numbers of statements in each module "
+                            "that weren't executed."))
+    
+    opts, args = parser.parse_args()
+    return opts, args
+
+def coverage_report(opts):
+    import coverage
+    from distutils2.tests.support import unittest
+    cov = coverage.coverage()
+    cov.load()
+
+    prefixes = ["runtests", "distutils2/tests", "distutils2/_backport"]
+    prefixes += ignore_prefixes(unittest)
+
+    try:
+        import docutils
+        prefixes += ignore_prefixes(docutils)
+    except ImportError:
+        # that module is completely optional
+        pass
+
+    try:
+        import roman
+        prefixes += ignore_prefixes(roman)
+    except ImportError:
+        # that module is also completely optional
+        pass
+
+    cov.report(omit_prefixes=prefixes, show_missing=opts.show_missing)
+
+
+def test_main():
+    opts, args = parse_opts()
+    verbose = not opts.quiet
+    ret = 0
+
+    if opts.coverage or opts.report:
+        import coverage
+
+    if opts.coverage:
+        cov = coverage.coverage()
+        cov.erase()
+        cov.start()
+    if not opts.report:
+        ret = run_tests(verbose)
+    if opts.coverage:
+        cov.stop()
+        cov.save()
+
+    if opts.report or opts.coverage:
+        coverage_report(opts)
+
+    return ret
+
+def run_tests(verbose):
+    import distutils2.tests
+    from distutils2.tests import run_unittest, reap_children, TestFailed
+    from distutils2._backport.tests import test_suite as btest_suite
+    # XXX just supporting -q right now to enable detailed/quiet output
+    if len(sys.argv) > 1:
+        verbose = sys.argv[-1] != '-q'
+    else:
+        verbose = 1
+    try:
+        try:
+            run_unittest([distutils2.tests.test_suite(), btest_suite()],
+                         verbose_=verbose)
+            return 0
+        except TestFailed:
+            return 1
+    finally:
+        reap_children()
+
+if __name__ == "__main__":
+    try:
+        from distutils2.tests.support import unittest
+    except ImportError:
+        sys.stderr.write('Error: You have to install unittest2')
+        sys.exit(1)
+
+    sys.exit(test_main())
+
diff --git a/src/runtests.py b/src/runtests.py
--- a/src/runtests.py
+++ b/src/runtests.py
@@ -1,6 +1,6 @@
 """Tests for distutils2.
 
-The tests for distutils2 are defined in the distutils2.tests package;
+The tests for distutils2 are defined in the distutils2.tests package.
 """
 import sys
 
@@ -8,8 +8,7 @@
     import distutils2.tests
     from distutils2.tests import run_unittest, reap_children, TestFailed
     from distutils2._backport.tests import test_suite as btest_suite
-    # just supporting -q right now
-    # to enable detailed/quiet output
+    # XXX just supporting -q right now to enable detailed/quiet output
     if len(sys.argv) > 1:
         verbose = sys.argv[-1] != '-q'
     else:
@@ -17,7 +16,7 @@
     try:
         try:
             run_unittest([distutils2.tests.test_suite(), btest_suite()],
-                    verbose_=verbose)
+                         verbose_=verbose)
             return 0
         except TestFailed:
             return 1
@@ -28,7 +27,7 @@
     try:
         from distutils2.tests.support import unittest
     except ImportError:
-        print('Error: You have to install unittest2')
+        sys.stderr.write('Error: You have to install unittest2')
         sys.exit(1)
 
     sys.exit(test_main())
diff --git a/src/setup.py b/src/setup.py
--- a/src/setup.py
+++ b/src/setup.py
@@ -3,11 +3,14 @@
 __revision__ = "$Id$"
 import sys
 import os
+import re
 
-from distutils2.core import setup
+from distutils2 import __version__ as VERSION
+from distutils2 import log
+from distutils2.core import setup, Extension
+from distutils2.compiler.ccompiler import new_compiler
 from distutils2.command.sdist import sdist
 from distutils2.command.install import install
-from distutils2 import __version__ as VERSION
 from distutils2.util import find_packages
 
 f = open('README.txt')
@@ -31,6 +34,7 @@
 
 DEV_SUFFIX = '.dev%d' % get_tip_revision('..')
 
+
 class install_hg(install):
 
     user_options = install.user_options + [
@@ -62,10 +66,141 @@
             self.distribution.metadata.version += DEV_SUFFIX
         sdist.run(self)
 
+
+# additional paths to check, set from the command line
+SSL_INCDIR = ''   # --openssl-incdir=
+SSL_LIBDIR = ''   # --openssl-libdir=
+SSL_DIR = ''      # --openssl-prefix=
+
+def add_dir_to_list(dirlist, dir):
+    """Add the directory 'dir' to the list 'dirlist' (at the front) if
+    'dir' actually exists and is a directory.  If 'dir' is already in
+    'dirlist' it is moved to the front.
+    """
+    if dir is not None and os.path.isdir(dir) and dir not in dirlist:
+        if dir in dirlist:
+            dirlist.remove(dir)
+        dirlist.insert(0, dir)
+
+
+def prepare_hashlib_extensions():
+    """Decide which C extensions to build and create the appropriate
+    Extension objects to build them.  Return a list of Extensions.
+    """
+    # this CCompiler object is only used to locate include files
+    compiler = new_compiler()
+
+    # Ensure that these paths are always checked
+    if os.name == 'posix':
+        add_dir_to_list(compiler.library_dirs, '/usr/local/lib')
+        add_dir_to_list(compiler.include_dirs, '/usr/local/include')
+
+        add_dir_to_list(compiler.library_dirs, '/usr/local/ssl/lib')
+        add_dir_to_list(compiler.include_dirs, '/usr/local/ssl/include')
+
+        add_dir_to_list(compiler.library_dirs, '/usr/contrib/ssl/lib')
+        add_dir_to_list(compiler.include_dirs, '/usr/contrib/ssl/include')
+
+        add_dir_to_list(compiler.library_dirs, '/usr/lib')
+        add_dir_to_list(compiler.include_dirs, '/usr/include')
+
+    # look in command line supplied paths
+    if SSL_LIBDIR:
+        add_dir_to_list(compiler.library_dirs, SSL_LIBDIR)
+    if SSL_INCDIR:
+        add_dir_to_list(compiler.include_dirs, SSL_INCDIR)
+    if SSL_DIR:
+        if os.name == 'nt':
+            add_dir_to_list(compiler.library_dirs, os.path.join(SSL_DIR, 'out32dll'))
+            # prefer the static library
+            add_dir_to_list(compiler.library_dirs, os.path.join(SSL_DIR, 'out32'))
+        else:
+            add_dir_to_list(compiler.library_dirs, os.path.join(SSL_DIR, 'lib'))
+        add_dir_to_list(compiler.include_dirs, os.path.join(SSL_DIR, 'include'))
+
+    oslibs = {'posix': ['ssl', 'crypto'],
+              'nt': ['libeay32',  'gdi32', 'advapi32', 'user32']}
+
+    if os.name not in oslibs:
+        sys.stderr.write(
+            'unknown operating system, impossible to compile _hashlib')
+        sys.exit(1)
+
+    exts = []
+
+    ssl_inc_dirs = []
+    ssl_incs = []
+    for inc_dir in compiler.include_dirs:
+        f = os.path.join(inc_dir, 'openssl', 'ssl.h')
+        if os.path.exists(f):
+            ssl_incs.append(f)
+            ssl_inc_dirs.append(inc_dir)
+
+    ssl_lib = compiler.find_library_file(compiler.library_dirs, oslibs[os.name][0])
+
+    # find out which version of OpenSSL we have
+    openssl_ver = 0
+    openssl_ver_re = re.compile(
+        '^\s*#\s*define\s+OPENSSL_VERSION_NUMBER\s+(0x[0-9a-fA-F]+)' )
+    ssl_inc_dir = ''
+    for ssl_inc_dir in ssl_inc_dirs:
+        name = os.path.join(ssl_inc_dir, 'openssl', 'opensslv.h')
+        if os.path.isfile(name):
+            try:
+                incfile = open(name, 'r')
+                for line in incfile:
+                    m = openssl_ver_re.match(line)
+                    if m:
+                        openssl_ver = int(m.group(1), 16)
+                        break
+            except IOError:
+                pass
+
+        # first version found is what we'll use
+        if openssl_ver:
+            break
+
+    if (ssl_inc_dir and ssl_lib is not None and openssl_ver >= 0x00907000):
+
+        log.info('Using OpenSSL version 0x%08x from', openssl_ver)
+        log.info(' Headers:\t%s', ssl_inc_dir)
+        log.info(' Library:\t%s', ssl_lib)
+
+        # The _hashlib module wraps optimized implementations
+        # of hash functions from the OpenSSL library.
+        exts.append(Extension('_hashlib', ['_hashopenssl.c'],
+                              include_dirs = [ssl_inc_dir],
+                              library_dirs = [os.path.dirname(ssl_lib)],
+                              libraries = oslibs[os.name]))
+    else:
+        exts.append(Extension('_sha', ['shamodule.c']) )
+        exts.append(Extension('_md5',
+                              sources=['md5module.c', 'md5.c'],
+                              depends=['md5.h']) )
+
+    if (not ssl_lib or openssl_ver < 0x00908000):
+        # OpenSSL doesn't do these until 0.9.8 so we'll bring our own
+        exts.append(Extension('_sha256', ['sha256module.c']))
+        exts.append(Extension('_sha512', ['sha512module.c']))
+
+    def prepend_modules(filename):
+        return os.path.join('Modules', filename)
+
+    # all the C code is in the Modules subdirectory, prepend the path
+    for ext in exts:
+        ext.sources = [prepend_modules(fn) for fn in ext.sources]
+        if hasattr(ext, 'depends') and ext.depends is not None:
+            ext.depends = [prepend_modules(fn) for fn in ext.depends]
+
+    return exts
+
 setup_kwargs = {}
 if sys.version < '2.6':
     setup_kwargs['scripts'] = ['distutils2/mkpkg.py']
 
+if sys.version < '2.5':
+    setup_kwargs['ext_modules'] = prepare_hashlib_extensions()
+
 _CLASSIFIERS = """\
 Development Status :: 3 - Alpha
 Intended Audience :: Developers
@@ -77,26 +212,23 @@
 Topic :: System :: Systems Administration
 Topic :: Utilities"""
 
-setup (name="Distutils2",
-       version=VERSION,
-       summary="Python Distribution Utilities",
-       keywords=['packaging', 'distutils'],
-       author="Tarek Ziade",
-       author_email="tarek at ziade.org",
-       home_page="http://bitbucket.org/tarek/distutils2/wiki/Home",
-       license="PSF",
-       description=README,
-       classifier=_CLASSIFIERS.split('\n'),
-       packages=find_packages(),
-       cmdclass={'sdist': sdist_hg, 'install': install_hg},
-       package_data={'distutils2._backport': ['sysconfig.cfg']},
-       project_url=[('Mailing-list',
+setup(name="Distutils2",
+      version=VERSION,
+      summary="Python Distribution Utilities",
+      keywords=['packaging', 'distutils'],
+      author="Tarek Ziade",
+      author_email="tarek at ziade.org",
+      home_page="http://bitbucket.org/tarek/distutils2/wiki/Home",
+      license="PSF",
+      description=README,
+      classifier=_CLASSIFIERS.split('\n'),
+      packages=find_packages(),
+      cmdclass={'sdist': sdist_hg, 'install': install_hg},
+      package_data={'distutils2._backport': ['sysconfig.cfg']},
+      project_url=[('Mailing list',
                     'http://mail.python.org/mailman/listinfo/distutils-sig/'),
-                    ('Documentation',
-                     'http://packages.python.org/Distutils2'),
-                    ('Repository', 'http://hg.python.org/distutils2'),
-                    ('Bug tracker', 'http://bugs.python.org')],
-       **setup_kwargs
-       )
-
-
+                   ('Documentation',
+                    'http://packages.python.org/Distutils2'),
+                   ('Repository', 'http://hg.python.org/distutils2'),
+                   ('Bug tracker', 'http://bugs.python.org')],
+      **setup_kwargs)
diff --git a/src/tests.sh b/src/tests.sh
--- a/src/tests.sh
+++ b/src/tests.sh
@@ -1,28 +1,41 @@
 #!/bin/sh
-echo -n "Running tests for Python 2.4..."
-python2.4 runtests.py -q > /dev/null 2> /dev/null
+echo -n "Running tests for Python 2.4... "
+rm -rf *.so
+python2.4 setup.py build_ext -i -q 2> /dev/null > /dev/null
+python2.4 -Wd runtests.py -q 2> /dev/null
+rm -rf *.so
 if [ $? -ne 0 ];then
     echo "Failed"
-    exit $1
+    exit 1
 else
     echo "Success"
 fi
 
-echo -n "Running tests for Python 2.5..."
-python2.5 runtests.py -q > /dev/null 2> /dev/null
+echo -n "Running tests for Python 2.5... "
+python2.5 setup.py build_ext -i -q 2> /dev/null > /dev/null
+python2.5 -Wd runtests.py -q 2> /dev/null
+rm -rf *.so
 if [ $? -ne 0 ];then
     echo "Failed"
-    exit $1
+    exit 1
 else
     echo "Success"
 fi
 
-echo -n "Running tests for Python 2.6..."
-python2.6 runtests.py -q > /dev/null 2> /dev/null
+echo -n "Running tests for Python 2.6... "
+python2.6 -Wd -bb -3 runtests.py -q 2> /dev/null
 if [ $? -ne 0 ];then
     echo "Failed"
-    exit $1
+    exit 1
 else
     echo "Success"
 fi
 
+echo -n "Running tests for Python 2.7... "
+python2.7 -Wd -bb -3 runtests.py -q 2> /dev/null
+if [ $? -ne 0 ];then
+    echo "Failed"
+    exit 1
+else
+    echo "Success"
+fi

--
Repository URL: http://hg.python.org/distutils2


More information about the Python-checkins mailing list